Compare commits
2652 Commits
Author | SHA1 | Date | |
---|---|---|---|
bfe7e3afed | |||
75b1eaed0c | |||
7fb4c8358b | |||
5b3431b268 | |||
9b9d439fce | |||
62d1bc87ae | |||
51ed39804f | |||
d12110b47b | |||
accf0cd40e | |||
a9302468e7 | |||
3b9d822d72 | |||
f3a7de6b76 | |||
a807b231a7 | |||
d59a06344a | |||
e884341795 | |||
3a068970f9 | |||
1043d29dd5 | |||
6505a62801 | |||
7e016c44c1 | |||
e1b37f3cd3 | |||
0c24827a52 | |||
e6eee82fb9 | |||
c5cf78205a | |||
37b4f606c8 | |||
5824047ffa | |||
3dccc38f42 | |||
f613b03efd | |||
3c87e5d31e | |||
e26827b849 | |||
8eb10e991f | |||
f6f32914de | |||
1840505a0f | |||
29e532c096 | |||
0f0cbd4c1f | |||
0ece0b5b27 | |||
6a4fb6a271 | |||
4593538fc8 | |||
03b66ba7a5 | |||
954d4d701a | |||
ce2d58bb7d | |||
7af116fb00 | |||
0357921284 | |||
86974b046e | |||
a04a6f1e0d | |||
37420405c7 | |||
acf1946dac | |||
a52574b026 | |||
5d14724e66 | |||
d582bf7f1f | |||
c81cb59b4b | |||
ff03dd22fe | |||
aae892dfd1 | |||
f24211e8b5 | |||
e61d8e4dc8 | |||
403279e926 | |||
70983c7269 | |||
42f9eccb17 | |||
3c937532f2 | |||
863027cbe8 | |||
6b8fe283f0 | |||
7983977156 | |||
21cb459899 | |||
1b3ff96ffd | |||
42ceaa0015 | |||
90b330d4ba | |||
3be18b58bb | |||
22aaf56855 | |||
7537ea1ef5 | |||
2078133493 | |||
f60562c5a2 | |||
18cfe219fe | |||
129e959a3b | |||
e1b6020b76 | |||
d5c6a1b4d1 | |||
5b8d025440 | |||
6b6d6a01dc | |||
c5aff7cc1e | |||
b16829b0f9 | |||
f27716656c | |||
30cf73a22f | |||
884ac73078 | |||
718abaae0e | |||
62a402d05a | |||
ce5bb29c44 | |||
d535f73a58 | |||
d38696f411 | |||
d0b6f0124b | |||
b266068644 | |||
e0635955df | |||
5ec4cbf1cb | |||
e0380960fd | |||
64ba5e4ed1 | |||
733619ca74 | |||
9f9c829bc5 | |||
236764adf6 | |||
a80b425551 | |||
d7032d975c | |||
96decbac27 | |||
0fb6a2836b | |||
fe36471b8d | |||
6b28af6cc5 | |||
6306fb564b | |||
cdccb25fe3 | |||
b6452215c1 | |||
f1028fa66d | |||
9d2f0e4dc8 | |||
f997529cd4 | |||
495103f72e | |||
d80dee2a54 | |||
788fa40c2a | |||
ae75585b52 | |||
ff07714e8c | |||
f54fc16718 | |||
7298f9c273 | |||
672f5cf160 | |||
92cda68480 | |||
f41426f394 | |||
8dc3267925 | |||
709736d3f9 | |||
12f0d51c0c | |||
2e9403a324 | |||
ba17efa381 | |||
b8e0c8ebc6 | |||
1b05303250 | |||
51095c5a27 | |||
a02f67ed0e | |||
3a7961834a | |||
95842ee25f | |||
371c839573 | |||
1b8b044b52 | |||
a988111507 | |||
c56db0408b | |||
2321d9c065 | |||
ed085630db | |||
74d5c7c1a4 | |||
813ccc1381 | |||
b65f4c9536 | |||
012d8bb468 | |||
d7a137ad13 | |||
073aaf9b3b | |||
871d647c93 | |||
0ce3049579 | |||
de20258aa7 | |||
41c5e523b2 | |||
197be9cfd0 | |||
5b50b806ec | |||
62c59820cf | |||
0cf1fc72a3 | |||
5ce7874280 | |||
ba2b5c3a65 | |||
0a9081cb87 | |||
eea1e3b800 | |||
70364884a9 | |||
ea7f03770c | |||
bc1a7d2890 | |||
732bc53692 | |||
4055e34320 | |||
81b1406290 | |||
538092b727 | |||
4df4b43900 | |||
3e3b92d4c1 | |||
c7dc115365 | |||
e42050cc8a | |||
df17f5e899 | |||
a0045ece07 | |||
4c013e59f0 | |||
09d607f39c | |||
9418c62d95 | |||
ef50e5595e | |||
02bf086c09 | |||
77932061bc | |||
44bf32e729 | |||
0c986ba4d0 | |||
a28193430c | |||
0bec0046bb | |||
a96519cbdc | |||
458c2f38bc | |||
dc5402349e | |||
ae289c923c | |||
e0ef86340f | |||
1ca2c59f2e | |||
3ec92acfe7 | |||
656bfd36f6 | |||
562ae676a5 | |||
72292f4e03 | |||
536da704fc | |||
a0e03c41c0 | |||
a1053a4c5a | |||
9f441a9678 | |||
7870cf28e5 | |||
9939367db7 | |||
1210c3256d | |||
2e8d04aad0 | |||
3a0e4546c2 | |||
cd2419137d | |||
34ac8b3ec3 | |||
2837236d81 | |||
bc8336a4b1 | |||
397e7f0363 | |||
c5bbe42b57 | |||
c9105e525e | |||
1bed7754e0 | |||
5978882378 | |||
8ba51c7900 | |||
6d160a7b7e | |||
32409a361b | |||
2ceefea5f3 | |||
485f156e57 | |||
f794e49bb6 | |||
46f448dfba | |||
710156b9f1 | |||
6dcf34acdc | |||
d2f674e08f | |||
ec8cb056bf | |||
1893871a99 | |||
8837f06e4e | |||
5346dfc782 | |||
ddca838e46 | |||
45b0367d14 | |||
dce86edff0 | |||
84e91ec3b5 | |||
05b6969ee5 | |||
af949f5cdd | |||
7707af08e3 | |||
5db4fabcad | |||
cfca82ceb3 | |||
a1130dace0 | |||
95a0bd61a9 | |||
b324778be7 | |||
950f921c09 | |||
9dff1bac83 | |||
c17f6e37e5 | |||
e0e26a37b5 | |||
15a0c03664 | |||
7642e75b28 | |||
4b36d1e3ea | |||
20525bec29 | |||
430369feb8 | |||
5ac3e5c958 | |||
abc2a529fb | |||
c638fd9056 | |||
1dd96898e1 | |||
2cc9b0df06 | |||
3bd4ad7c93 | |||
a8ffdeca2b | |||
59b15c5f08 | |||
59cf8f678f | |||
088be050b2 | |||
5d5fcae501 | |||
970ec8187c | |||
76f66148db | |||
6bdd6ce571 | |||
dd96e1819d | |||
07702d3be7 | |||
b9dfcf6d2f | |||
f3f0652d2b | |||
aae60334d1 | |||
b38af948b9 | |||
06de728aa7 | |||
87384182a1 | |||
f583e617ec | |||
a2716f5cf6 | |||
55f928f845 | |||
2343aad088 | |||
dcaa907fed | |||
adcdf28d64 | |||
f28a7b9d08 | |||
6505b0c065 | |||
88ef02474f | |||
9ec32b2561 | |||
a5dd6b6cd7 | |||
46cc325f7c | |||
0fadb5a2be | |||
8b897ac714 | |||
a80b820e94 | |||
4efbf22089 | |||
5c913149c8 | |||
9265c319a9 | |||
cbe6eff2fa | |||
1f3d18ec12 | |||
89255e34bd | |||
a7b8092296 | |||
1feb630520 | |||
9f202caf93 | |||
651e511ff0 | |||
b46aca52ee | |||
0eae9355e6 | |||
4878f1a438 | |||
f986b4cd56 | |||
d5c6704475 | |||
fda9ca1e34 | |||
01c4ed232e | |||
dc8109658c | |||
8ce8055aeb | |||
a71b743ee3 | |||
c50e9ac418 | |||
7973b01e81 | |||
b977ac6df5 | |||
256fc322a8 | |||
33bf85a387 | |||
1f0fc61b53 | |||
89c945ecc8 | |||
c15962c6c1 | |||
9c2a3231c5 | |||
80840f1fdb | |||
7d02e1f866 | |||
d93cb751b0 | |||
c07fff7503 | |||
7896dd19c1 | |||
f5f2d33f93 | |||
4331eaae2e | |||
d5d4f87236 | |||
5d49433326 | |||
381d7413c8 | |||
e70a5a47ee | |||
6be7eed878 | |||
a6ac832f8f | |||
104863846b | |||
5883c30b84 | |||
cebaae76b4 | |||
d886d32bd8 | |||
8440c2819b | |||
8114d8778f | |||
f36c3a3f6c | |||
46c6cc2d2b | |||
ada5e88eb9 | |||
b724607e31 | |||
c3ea303a37 | |||
e89a10945c | |||
8ddc89fa01 | |||
a47bf72b07 | |||
34460dd77a | |||
c125c96e88 | |||
d2fd0fd8e8 | |||
74a875bdce | |||
edaa66f622 | |||
c392db7e0d | |||
6ac073e779 | |||
58d9ceda4b | |||
3781c64d41 | |||
4166d9ab7b | |||
435273e08a | |||
35a62d9787 | |||
75683039c5 | |||
1a609612f2 | |||
121a7a9e23 | |||
e593faf245 | |||
ec5bb944b2 | |||
deed49574a | |||
7cc39cd357 | |||
a714d5966e | |||
2b0252d4ae | |||
c78db5459e | |||
a27564ed70 | |||
df8df41621 | |||
d5a0d4b452 | |||
357b6ee991 | |||
4d2b5c2f42 | |||
f705cf09bc | |||
29f7ea752f | |||
9b7c8ea22d | |||
199a7df807 | |||
5186ad95d3 | |||
0546345182 | |||
90feaaf2df | |||
6d27ef5eea | |||
e4e8a51e5c | |||
3ddf413332 | |||
6eb36fb053 | |||
58239ff98f | |||
6e841a3b7e | |||
085e067fc1 | |||
c45fa016c0 | |||
16477a8f6c | |||
04e4900415 | |||
322f317a5b | |||
ea5020e188 | |||
cefa1662ef | |||
2a949fcb86 | |||
445f9e5f71 | |||
5a25ce8c1b | |||
670cf8ee07 | |||
6c5f6e8900 | |||
4ed4fb2314 | |||
f16989bea9 | |||
55d4064335 | |||
3b38a4c690 | |||
ada595663d | |||
b2de01b076 | |||
f3acf35aea | |||
3da1d6a464 | |||
b9df10c8bd | |||
ec1f73c827 | |||
cdc9f93f71 | |||
bcebb1920f | |||
81848e05f1 | |||
5ae69c079a | |||
0199d8a74f | |||
f49ad2ee03 | |||
7ed993b54e | |||
9934537e19 | |||
00d42d296e | |||
ad74fedfba | |||
730f714e97 | |||
3bec4a80b3 | |||
a84e4b0e07 | |||
198139feb4 | |||
de11017552 | |||
6fb837c529 | |||
c0c3892064 | |||
1a35fec134 | |||
f1247d2224 | |||
7992b7eb89 | |||
cd893e18d2 | |||
a5c8b166fe | |||
72a9b98ef0 | |||
cf022524d1 | |||
ebb0596c1a | |||
3de681d2d6 | |||
f7b0ba88da | |||
c95c81d42f | |||
9407596b12 | |||
c294c2d1df | |||
bd36f8e220 | |||
3a168ba6dd | |||
1919069b12 | |||
ba677a8cb7 | |||
e62e1d9701 | |||
b3330cb0da | |||
7d128c79a3 | |||
3cff23dae2 | |||
c3f0139f76 | |||
563fe8d515 | |||
38e20eb148 | |||
36571c5e22 | |||
c8eca4fb85 | |||
0e207aba6c | |||
45d1319891 | |||
39b7ac90d4 | |||
ef87bdf18a | |||
433a802c6e | |||
6a18079953 | |||
b937d33436 | |||
f7244fbf68 | |||
4e80d1fc79 | |||
b2f48eaeb8 | |||
160dd09fc2 | |||
80eea05deb | |||
5e89ba7079 | |||
ff7878217d | |||
807da6a035 | |||
4e2a958896 | |||
3ee0ec7cd0 | |||
22a2b7ac46 | |||
aa7c910e26 | |||
24a4bd3a1c | |||
fff52cb247 | |||
219c5b323d | |||
14278a9e35 | |||
dd013209e8 | |||
96d5438633 | |||
391ef64c22 | |||
d45a62b3a6 | |||
976e550aa7 | |||
668b19d119 | |||
5c48f0b458 | |||
689fe1e2c7 | |||
7d440402ad | |||
6daa457838 | |||
49d317b19a | |||
1b80ae0fca | |||
a113ecca8b | |||
61144f7a21 | |||
574af2c795 | |||
a4870d4834 | |||
78bbcac0ea | |||
ca9b2525c8 | |||
bb1c387a98 | |||
38ce37b6fd | |||
7fdc81236e | |||
fca40c1c6b | |||
d5d5d66e03 | |||
0481ae187a | |||
9de6927c3f | |||
30b01ef053 | |||
03b75bf2a9 | |||
f3f628410d | |||
9901ecda49 | |||
39becf607e | |||
f5955a4738 | |||
e018b30875 | |||
2dbd775cf3 | |||
a5051327db | |||
8140f5136d | |||
f04703f09b | |||
2faf8332ee | |||
e0e3d4d8fe | |||
ba81ad1ac3 | |||
eefb259ddf | |||
9b8add1961 | |||
e186523878 | |||
021e6c02d7 | |||
cba3d68063 | |||
7e2d78bab5 | |||
4b12c85d91 | |||
0ebf04a021 | |||
d55f47077a | |||
11df4845b7 | |||
7f438425aa | |||
c470f05abf | |||
141e94369e | |||
257970c27d | |||
67ac3da8a0 | |||
c1e5179b5f | |||
7a651bdc53 | |||
f33f596584 | |||
c8d8046412 | |||
4d334b645d | |||
58d3779efb | |||
3691f3a296 | |||
463b4fbe0c | |||
9f1c79a5ec | |||
70573b6f31 | |||
6ea1234a3b | |||
434f639b0c | |||
bf04becc9e | |||
b2082bfde7 | |||
78984eea3a | |||
8422e3ac01 | |||
cfce54fe46 | |||
a7c9b2f172 | |||
eb31a951a1 | |||
ccfe605920 | |||
878614ff68 | |||
25e23e50ca | |||
64c51a70a3 | |||
e4296c48c8 | |||
dd3848d7b1 | |||
56ac1a0813 | |||
cbe5af235c | |||
f932ffcc5b | |||
0a50c5bf40 | |||
e416e5d301 | |||
3227859992 | |||
01139c3b50 | |||
ee3e65d759 | |||
c85867395d | |||
bf2ce54076 | |||
07de285299 | |||
8c194cd245 | |||
8cbec3d5a0 | |||
cdf05e74c8 | |||
b98b4f1027 | |||
fc11dfd6b4 | |||
483a5b6cae | |||
df1b7f1656 | |||
a566d1c5de | |||
11c8237d8b | |||
6dc19c85a8 | |||
6e07c11f65 | |||
8da6667816 | |||
81fedbf03c | |||
0516055b31 | |||
e3f8d99087 | |||
22aebc2215 | |||
3653e9d5e3 | |||
40f16b6d62 | |||
a61daa40dc | |||
ee67b5f1ad | |||
aa3633d2d7 | |||
7b805a38b9 | |||
c8d8dda79a | |||
c440f33122 | |||
b0c866bfaa | |||
dd578354c4 | |||
fdac978c84 | |||
3b81ada1c3 | |||
4ee29b388d | |||
52dc9068e5 | |||
4f1ea712da | |||
849b92665e | |||
d193ed9eeb | |||
ee003cd9ee | |||
127b094c41 | |||
b2285be5bc | |||
6ede02bb84 | |||
821c424b66 | |||
5450e0edf3 | |||
e05ccc0e55 | |||
4f3786c2e1 | |||
107977f3d1 | |||
813663485d | |||
64585d8f78 | |||
756d933d1a | |||
83deccf266 | |||
aaef448959 | |||
7e96077ed7 | |||
c44f931059 | |||
15084c8d6d | |||
822c5a530e | |||
6eb641ae56 | |||
9eb14e6b9b | |||
7cc42269a9 | |||
c45442760c | |||
83f0611200 | |||
d95625ed88 | |||
5622bcc563 | |||
deb9c98630 | |||
2c1e887c8d | |||
4a13d72997 | |||
2d5820e910 | |||
6cb857b895 | |||
590875d022 | |||
8f30237765 | |||
cb0339b492 | |||
e427174ff0 | |||
fc4c57f0d9 | |||
9ad4ae5340 | |||
0f48e0fc06 | |||
37ad1b40d8 | |||
514e7ae6a0 | |||
ab2f3bb5bc | |||
34230bfcf4 | |||
c390e211ee | |||
b0d69db878 | |||
b373beee21 | |||
8c99edd3c5 | |||
8036272e87 | |||
6f50809457 | |||
bb386a1162 | |||
80054e4db2 | |||
1630a23fb0 | |||
4e75419e08 | |||
089018015a | |||
feb6f285ce | |||
cb12c51afc | |||
c5c426ecbc | |||
74f7039abf | |||
57b905be24 | |||
34794cc4af | |||
38f59fdf39 | |||
527c1113f1 | |||
04dbe28793 | |||
968366c2ae | |||
7f5dea28bb | |||
d4979974b4 | |||
9539230915 | |||
25cfa26e7b | |||
0ff8891c66 | |||
58d2c15ffa | |||
82699cc297 | |||
08d008a5aa | |||
9e17ff884f | |||
1c567232e3 | |||
cba2608c1c | |||
e5c42f68c2 | |||
02b4468bcd | |||
746f6945da | |||
5cab4d8864 | |||
5cedfcbfaf | |||
c06abd9b6b | |||
e08d97825d | |||
d61323aa6d | |||
5ae044db93 | |||
c6e4fdac7e | |||
9669377b06 | |||
c8c3fe1023 | |||
85bddbed86 | |||
318a2a429c | |||
07359865c6 | |||
2fd7338cd3 | |||
ce3cfa212c | |||
b29c99656e | |||
45b0762096 | |||
aceafb28f6 | |||
928939cbe3 | |||
2462b8e524 | |||
263a72879d | |||
70620d5137 | |||
79d5beff8d | |||
fa3caf091a | |||
93707560cb | |||
5c6f325071 | |||
b9a17f5999 | |||
ab23f542c6 | |||
fa98bf1ee7 | |||
3cc987a5b4 | |||
d1a1b8b6e8 | |||
0d08e082be | |||
386fb28a35 | |||
79b79b752c | |||
a116778402 | |||
4c1c26a0e6 | |||
9e1653ebb4 | |||
99dd9874df | |||
24193163e1 | |||
94d164c0f2 | |||
efef2e95ab | |||
5efa725e92 | |||
42c00213ce | |||
b7d1b9ac61 | |||
52d06ed814 | |||
46a1c855a9 | |||
31dc82b1a6 | |||
4e27e132a0 | |||
a5b0b4800a | |||
e6244c937a | |||
3cbf8d4993 | |||
f3279a0697 | |||
8cac61f0be | |||
236c196e68 | |||
6c09efd630 | |||
cc92e1b0b6 | |||
c90f2c3ac6 | |||
6bdd27a08e | |||
80ff0b1418 | |||
a8943ce1ee | |||
71d35dc70b | |||
9daf2a2996 | |||
f5d6aea942 | |||
5850447fd0 | |||
e12a7a4415 | |||
81d84b8d02 | |||
bae0a0530b | |||
cbf4159c7e | |||
4a1d082614 | |||
fd8b4c5368 | |||
5872c34315 | |||
b60fe08d44 | |||
6a51eda361 | |||
96e8217b00 | |||
e6cb6cae11 | |||
980f03dd20 | |||
20c281d6f8 | |||
66f9fed2f2 | |||
68b7aa0a4d | |||
f916ce8752 | |||
fdbd8d9d2b | |||
4208c2c72a | |||
5cc91965d0 | |||
c74f852364 | |||
c1b3a3adb4 | |||
7096f02b88 | |||
70a11935a8 | |||
f601135cc0 | |||
884fe0d574 | |||
ffcbf97dcc | |||
f55fc51e9b | |||
df0f9259c0 | |||
bb7a321c6e | |||
2d69d63efe | |||
479843f56b | |||
6e1639551b | |||
2367903ac6 | |||
30607c34a1 | |||
817947c928 | |||
29926a59bb | |||
d92ae530d7 | |||
e0e428ce38 | |||
d1db7a0e23 | |||
c27ebde575 | |||
a99cd16422 | |||
1f6b8f9d2b | |||
a33b804923 | |||
f527958cb3 | |||
b1bdc6f745 | |||
8dacbafc8b | |||
70fa92f22c | |||
3482076a20 | |||
f7239f7f8a | |||
20ba787c00 | |||
669eef92eb | |||
c6d9edb78f | |||
347ae0a9ad | |||
dbd01d35e6 | |||
b24c09665f | |||
fa96ebd382 | |||
52d43f843b | |||
90376749f0 | |||
5cf2a8b5fc | |||
f72ac94c11 | |||
bd3a693e70 | |||
298871e207 | |||
ce958f4ffd | |||
2de150bfc8 | |||
40970a1a87 | |||
8aae652be1 | |||
37a117d2ef | |||
debc1659e1 | |||
23e411c42b | |||
88feebe499 | |||
e8b871ac72 | |||
9f30c6d94b | |||
c4c1e75de8 | |||
c1763cc4b0 | |||
ee096edfff | |||
464b9ebc95 | |||
7705f290ca | |||
7439fd6bcb | |||
6fe626ab9a | |||
a12f892841 | |||
aa3ea79f94 | |||
ef53455b66 | |||
ba46ff6692 | |||
0e916244f0 | |||
1e4ee02cbc | |||
cedc7754d9 | |||
fece9e207b | |||
cca052ccc9 | |||
5be9472912 | |||
f60b09568c | |||
be765f8d88 | |||
8d6aecd944 | |||
aef5349aee | |||
ac993aa31f | |||
fce323b945 | |||
5558f3d2cc | |||
08f8623cb7 | |||
f8a137a26e | |||
3cba359d38 | |||
b8d7aedb2c | |||
55c7b291e1 | |||
472d931b4b | |||
ce892c9e77 | |||
e14b998da3 | |||
2c9452efaf | |||
97a7af855f | |||
5be8545edc | |||
0a04c3a2a7 | |||
dcfc15a0a1 | |||
841c2e9166 | |||
07392d493c | |||
acd50969e0 | |||
074b53eb6b | |||
c4cfec1e94 | |||
b0269e6dfc | |||
43b9d9484d | |||
69bbb29328 | |||
577069cfb4 | |||
fe94c3609e | |||
173aed7fd8 | |||
335bec68fb | |||
bbb7e9f5c7 | |||
086304f7f2 | |||
bd7065eece | |||
15aaff7c1c | |||
e048bce13e | |||
ddfb449b28 | |||
5322155b19 | |||
45ce72593a | |||
b813c867b5 | |||
f7d7d76ee8 | |||
7e5076b068 | |||
dbe0553b4a | |||
9be48f1dc4 | |||
6043444e4e | |||
487e352642 | |||
cc5f82bfac | |||
4d4dfab388 | |||
b83f9be1bd | |||
6ae3b183fd | |||
f12117c532 | |||
70768189ba | |||
30b266622c | |||
25b306b7e1 | |||
9af1b00df5 | |||
51c27e2748 | |||
b9e2c3524c | |||
3f6cc17818 | |||
5909af9878 | |||
64576f4c4c | |||
21dbd28a2f | |||
b57fee1a44 | |||
cabd887866 | |||
863a168cb5 | |||
5ee4fb3522 | |||
bb8ac9b99a | |||
2f10fa8b61 | |||
46a8e18841 | |||
6c53e68a52 | |||
392452d422 | |||
99ed0b6c2c | |||
e6d057fe6d | |||
9ad6eb11a3 | |||
32c2ad2bbd | |||
f2ca9a6b31 | |||
50d40257fe | |||
d90eff64d0 | |||
e6dc34fe10 | |||
745c331311 | |||
1ea2e85415 | |||
4a2df30f92 | |||
922220c11e | |||
56d5035c63 | |||
4abf3a20c6 | |||
5912ef3b45 | |||
9931c9a286 | |||
1da834f650 | |||
fb677a7489 | |||
404796d4b2 | |||
5b9bfe8891 | |||
779bc2c63d | |||
cae1ba7cd8 | |||
8ce78dcc54 | |||
afe088dba1 | |||
e899e31745 | |||
dabb84f62a | |||
9b8096c699 | |||
ac5b74301e | |||
609b30110b | |||
35d200356f | |||
0cd1d7aa7e | |||
ade7526f5f | |||
a7b1700d42 | |||
c0b8c53e69 | |||
e86fbc697f | |||
e5622ce824 | |||
99d9868116 | |||
107fa6b4f7 | |||
9062d28704 | |||
e68dcea6bc | |||
787c5a515f | |||
ce2df4b36f | |||
fcb9733df0 | |||
245928a064 | |||
589d160515 | |||
380e76a2e3 | |||
9ac6114b63 | |||
d558ff305a | |||
89aaac3a70 | |||
456999eee6 | |||
16e3b786fc | |||
126bbd67f7 | |||
38e1d44dbb | |||
9fb80a2687 | |||
d5d224d89a | |||
4708ce4226 | |||
a0580946dc | |||
e68fd7c1e3 | |||
369419870e | |||
0938f80b41 | |||
9f462dcc27 | |||
96008d3bb2 | |||
c05f744ec2 | |||
3df31579a1 | |||
f1d3481a29 | |||
cf3aad9c41 | |||
7b1e68bfa8 | |||
82256a2fe8 | |||
016b3448e8 | |||
2dde26e485 | |||
2999afe781 | |||
bfc5a6488b | |||
4af22d663a | |||
ba8322aa5c | |||
e4bd82190a | |||
695734636f | |||
bdac2a5f4d | |||
f8ea6212c7 | |||
5e9b26dbef | |||
67c99369ab | |||
d35c2db41e | |||
dff5fea976 | |||
ab6c7244fc | |||
353b51f11e | |||
76050880f1 | |||
396a7994f9 | |||
e29259cd55 | |||
79a728d7d2 | |||
98444bd865 | |||
3f75e1cb1d | |||
4f327120ac | |||
cc8456ec6b | |||
f8ad1a052f | |||
372ce588cd | |||
12632ec404 | |||
e777c26a14 | |||
1d38248194 | |||
d52868c6c5 | |||
29c1639b1d | |||
653a953d2f | |||
9f5537d936 | |||
21a7af535c | |||
a241d3f187 | |||
8f045af867 | |||
6d4efc0443 | |||
e2e6d00064 | |||
e682c3e9b8 | |||
fe347262e9 | |||
c993209b76 | |||
2cf4d5f8ec | |||
c6ab9718ae | |||
3e3ef03855 | |||
c2f37716e5 | |||
e9bfd43ff7 | |||
2958ffa73f | |||
3d11d044d2 | |||
7eb0b1c69a | |||
8d39a9ea61 | |||
cb796dbdfb | |||
4254c20eda | |||
094b57bb23 | |||
9ca4f19aea | |||
a29d88c313 | |||
a1ed8154f7 | |||
6c45a990ef | |||
8dfa3393dc | |||
d12323396f | |||
48d34741f3 | |||
76203018e9 | |||
7956e6f04e | |||
668342b470 | |||
0a0ce74105 | |||
f6a3fa55b1 | |||
718a9a3559 | |||
5485c5aaed | |||
afcdf4b9cf | |||
a3f5ea3598 | |||
fef60a9da0 | |||
576867605d | |||
d1626d20bd | |||
c890aa18f7 | |||
6c7d040439 | |||
d03ae34b61 | |||
b9547adce7 | |||
7f32c6464d | |||
209a1650e4 | |||
b142e2a7a7 | |||
6010ce0dc5 | |||
a7a331a26e | |||
fda2c116be | |||
d2f3dbaa29 | |||
e9d4793b1e | |||
13c7efa058 | |||
1598d65824 | |||
028e086960 | |||
04b39294ba | |||
3a446c410c | |||
35edea2141 | |||
ce1a4857cc | |||
dfdf739282 | |||
7086b1e65c | |||
f2d445d27b | |||
29d362a6e0 | |||
c8adc4e356 | |||
8467daa0ec | |||
13e14b712e | |||
27dfe30fe6 | |||
d89a541a8d | |||
42350e689d | |||
b7d5fee995 | |||
fd099166c6 | |||
b4be28b9bb | |||
19a8d51bf3 | |||
5d1aac3c53 | |||
87b0d3fe11 | |||
98891a036d | |||
b142a6da5b | |||
1c0139c5f4 | |||
1b7ae4321d | |||
7f991ed897 | |||
5ed7eb9d53 | |||
c00f96c7ca | |||
81333515e0 | |||
be3c9abcfe | |||
177f17450a | |||
150a3adaaf | |||
76146e0f17 | |||
4500e345b3 | |||
e885f3c26a | |||
e6e92f2b0e | |||
03b48554b1 | |||
f229574758 | |||
7b446dd30f | |||
a5be974f1a | |||
952615eadb | |||
93894f62ff | |||
2051de51b5 | |||
2ba3de79d8 | |||
d35c985d2e | |||
b18466da84 | |||
90714a93f3 | |||
990cfb7b33 | |||
aacf7938ae | |||
2e0f818905 | |||
63b6c6685c | |||
610b971117 | |||
318f657c31 | |||
482d3a1d76 | |||
b516dd970f | |||
d9463620e6 | |||
385c452ddf | |||
a89df42561 | |||
08cd5d52fa | |||
d0e668e1d8 | |||
eeb95f89e1 | |||
e4e0c27e1c | |||
b638a6ae95 | |||
74b98af8e5 | |||
6a474a0125 | |||
224304813a | |||
4caf06bc99 | |||
15593b5c09 | |||
e7e56eb1e3 | |||
43bf601f12 | |||
d5109f024b | |||
7f441f5b87 | |||
0aac85dda1 | |||
4777a45722 | |||
29e6e170cc | |||
01db80d19a | |||
8b28ed67ae | |||
f35a30d371 | |||
1a8de111a3 | |||
9bb62e1bba | |||
b2789fef0f | |||
46fe7e77b3 | |||
99e1c13e80 | |||
a6e65dfc7a | |||
1471eddc7e | |||
89fd84d916 | |||
0e17eee096 | |||
81f1305270 | |||
2ec4310104 | |||
24e4db676a | |||
6e21a9829d | |||
4e99e10cbd | |||
500a7eceab | |||
1dad35ca76 | |||
8b3093e758 | |||
a406aeb3ea | |||
970e4b020e | |||
a4f4f2891c | |||
a6b13487f0 | |||
56ae4e5b6a | |||
54c26b05fa | |||
bd376d5217 | |||
2592c69021 | |||
4a2b25e841 | |||
ba4af1a890 | |||
9b17cde019 | |||
7a8c963722 | |||
82d1dbf173 | |||
aefb8b353e | |||
5b75869c0b | |||
1288b92615 | |||
7eecf454e8 | |||
ccf7d1e0c4 | |||
e4b5cd23e6 | |||
819296cc9c | |||
67c7d80f41 | |||
919f8c54d7 | |||
cbaf71f581 | |||
f118c602e6 | |||
ff2f40f5e1 | |||
9758c9b2a2 | |||
09e4e100a2 | |||
bdd5c180d3 | |||
749b085063 | |||
c5a796ad8e | |||
12d8409e65 | |||
8db89b4cfb | |||
31dc92dafe | |||
01b90809e8 | |||
0a7383a4e1 | |||
5e86a34925 | |||
ece206b81c | |||
88341b923a | |||
5d27633821 | |||
a9d4370ad4 | |||
8a0318c779 | |||
8e754d9a56 | |||
0e209dcb02 | |||
6f2d6425a2 | |||
b46c4a81e0 | |||
b6aa33f3a6 | |||
ab68d9198d | |||
32eebd3ca7 | |||
3f2e5633b4 | |||
a99b9e80b8 | |||
42ed7e4626 | |||
ae910eea62 | |||
d35920f356 | |||
c29e7477f0 | |||
86bbc7939c | |||
dab091a7b7 | |||
513d93cfc7 | |||
d7478c14fd | |||
297c593edd | |||
6439ef11aa | |||
d6479e133d | |||
50bbf2aacc | |||
41032aaac2 | |||
4e08f28246 | |||
46c57e120f | |||
6b52ee61ae | |||
61fbc5a791 | |||
22365205f9 | |||
c6515c1dad | |||
8201d1df02 | |||
ddf168c536 | |||
c9f81f56e9 | |||
570264e1e9 | |||
325e58d98c | |||
23a2960aa3 | |||
888a87463e | |||
2ebaf46095 | |||
3a95a3b7c1 | |||
c4edffb388 | |||
5eec7cc788 | |||
fb4cf0b75d | |||
5584884fe6 | |||
3bb1068ef0 | |||
b1b85313ae | |||
92dfd659f1 | |||
d87f743a2b | |||
aec3e7b0fc | |||
243600b75b | |||
3d89d126d0 | |||
54281e53a1 | |||
6befd2be81 | |||
1f0ca9ed92 | |||
f3db9c3920 | |||
83ceb26151 | |||
32cdfb871c | |||
801e7da5ee | |||
2cf1ab7ec5 | |||
d049c1afaf | |||
9f45389bc1 | |||
d875481969 | |||
17a1e1245c | |||
06d28c3eec | |||
684982c25c | |||
421522a61a | |||
f3b29d67f4 | |||
2ee5c6b2a1 | |||
5083772c6f | |||
fd51e5df47 | |||
3405fd91c6 | |||
194822f11e | |||
98963d4cdf | |||
804ef36b20 | |||
3c6ff8fddf | |||
511893d535 | |||
e05fe77bfe | |||
2b7b9a2abb | |||
6db3f68ae1 | |||
3773f2e7a5 | |||
1f8bffc15a | |||
afaef4e83b | |||
b746f723cb | |||
cfaf44f57b | |||
eed541201e | |||
a8bcb85f7b | |||
981e9cf290 | |||
3ac398ac49 | |||
2d0728395f | |||
722194405a | |||
0868a5e534 | |||
a86b9c82ce | |||
f8642548c8 | |||
27873f7ed4 | |||
b3eda6bcbb | |||
1c7799e292 | |||
aecd158d3c | |||
87d35f0d16 | |||
c089f9b59f | |||
82d7f9f5a4 | |||
8bc529be3d | |||
924c1634d3 | |||
03d077e915 | |||
303628bb05 | |||
3b92ec8e82 | |||
545944cb0d | |||
c90a88b6b6 | |||
89e45a61b3 | |||
2aff7bac4a | |||
124097d3a5 | |||
c520faed6d | |||
5bdb3703ed | |||
2901039a48 | |||
dfa220ef02 | |||
f26be00571 | |||
83654a193e | |||
b2a5d8daf4 | |||
19ee736e1d | |||
fda3f1352e | |||
d194b02e28 | |||
aaba99dc10 | |||
93a2e0f777 | |||
71f3c6b461 | |||
aabcca5059 | |||
7e67fd8c79 | |||
fecf1ffcb9 | |||
fafc9cf9ca | |||
3111e6a721 | |||
e436f471a0 | |||
28f84902f6 | |||
ebee50eedc | |||
e0ef09dfe1 | |||
d7992ab29d | |||
60f19f305e | |||
d99976f5d7 | |||
db158a5735 | |||
ea3be17220 | |||
f26049009e | |||
787234a53a | |||
fc3a64056c | |||
6ebc9abb80 | |||
80e9eed35a | |||
23b3990f99 | |||
41276403df | |||
e3e9e39498 | |||
f315025a8d | |||
8e43d97133 | |||
c97a47dc62 | |||
7ccafdc993 | |||
0dca9cb6b8 | |||
be3d780720 | |||
0f59a1dde1 | |||
7c6bb80cee | |||
62841c5b23 | |||
aad4f8d1f7 | |||
11c44c676c | |||
370c3aa598 | |||
1cdadafdf8 | |||
dd6f670dec | |||
9ff364b0d3 | |||
58a5331f7b | |||
ed261f0af9 | |||
c01b475cbf | |||
3d4feeec8d | |||
a1800ec23f | |||
8a4f1c66f8 | |||
b187231b0e | |||
60b38de69f | |||
600c49f7f0 | |||
e7380e70a3 | |||
3df8594f19 | |||
ee4a829293 | |||
1862f3c124 | |||
777ab3416f | |||
ecf5ab75e7 | |||
4f6d964217 | |||
06019f01e3 | |||
ddde885084 | |||
be8c6f218c | |||
9eb35ea7c8 | |||
d2fdbec41d | |||
2dd372600c | |||
eda6cf11ef | |||
68facd6b93 | |||
87002fb8f8 | |||
6a50fa35ec | |||
6541570969 | |||
4b0ceea894 | |||
8c0816c166 | |||
be769d07f1 | |||
3a9d58e31c | |||
7024acac06 | |||
6131346e2f | |||
eed73c9078 | |||
795d6f35ee | |||
ebd46705d5 | |||
72d2ca234e | |||
2246c3359b | |||
242fb156a2 | |||
208ed73e59 | |||
4441b37338 | |||
941d75824a | |||
ec9ddc4f22 | |||
98b6f90172 | |||
7e280de361 | |||
7bd8bd13fe | |||
09e85e948c | |||
9ec1c00887 | |||
c6bcb6228b | |||
a24d589845 | |||
ebbcc9f6da | |||
23fc453fae | |||
aad6f74db6 | |||
07dcefabcb | |||
40c68595d7 | |||
fe9a4fece4 | |||
098327f128 | |||
0873b8d304 | |||
c9eb584ac8 | |||
10493bd44a | |||
9e35230467 | |||
81e326571b | |||
1b2a7de4e2 | |||
11216d200c | |||
777be6a48d | |||
5765a1fdf1 | |||
29dcb9d274 | |||
8674ac4f68 | |||
684b8f24f3 | |||
1ca2be0039 | |||
8c41ff68f7 | |||
931d6c280a | |||
ee0fb2d0e0 | |||
c496ad1237 | |||
277fa21f5f | |||
1cf949226e | |||
be3fae6511 | |||
5932f36285 | |||
30abb65368 | |||
64a06b5ed6 | |||
69d18f17a5 | |||
4c7d3a103c | |||
10320bdeb4 | |||
4261dcff39 | |||
a091245793 | |||
ca282f9fb3 | |||
7cf2c3be0f | |||
f65d506f26 | |||
333dbca01e | |||
42eb265624 | |||
8a65726e9d | |||
1b0ca47682 | |||
8e3f5c3305 | |||
3c0a6987cd | |||
e37f70b9f7 | |||
03c148ce50 | |||
4817f0312d | |||
b70a82c609 | |||
25d1e0c4e6 | |||
8e3356f11a | |||
1b559c7776 | |||
d5583f0f02 | |||
bedd3c50b6 | |||
43a7af3f44 | |||
9db27c6acc | |||
42c81395b3 | |||
3b13e692d2 | |||
0331f5a1eb | |||
8a7e117f6b | |||
9b984cedac | |||
dd9e30b24a | |||
6a93688b2e | |||
3ab17a97a8 | |||
f21ae66265 | |||
afa1a5e932 | |||
050768c266 | |||
cda2bfc240 | |||
2f167b1512 | |||
ba3ac85356 | |||
ec29cedeb7 | |||
064ae49d2b | |||
247f99ce2f | |||
7b6d269904 | |||
87a0482b8b | |||
e899699918 | |||
bdf464e792 | |||
c410bb4ecb | |||
a720bcc637 | |||
369a8cdc74 | |||
3f4e55be4f | |||
9171f471ab | |||
2186b134a4 | |||
f371ec210c | |||
afcd669d2f | |||
fbf542d205 | |||
13184eb8e9 | |||
ddf1e1ccee | |||
5ac4e73697 | |||
6be59b53f1 | |||
bb54fec907 | |||
0b81b283bf | |||
e2ab2aea32 | |||
c3ceefbafb | |||
e7cf9932a9 | |||
0c9d03f5df | |||
92aa24ae34 | |||
97a74d5c1f | |||
256f8094f5 | |||
1e2f0ab308 | |||
af2cf2734d | |||
ec62d8e973 | |||
3225f514f6 | |||
2d63c86022 | |||
2dcff83be7 | |||
afb9ebcd99 | |||
92d7e44525 | |||
a517f442ea | |||
311758233b | |||
6e086eb808 | |||
7e8644430c | |||
70a8f6743a | |||
69eafd0e11 | |||
6b6a095b91 | |||
d5a2185030 | |||
26f31e9288 | |||
e654e66839 | |||
bb4861cf0d | |||
01505910f4 | |||
ab766a0598 | |||
51c664a678 | |||
3c4b45c9e7 | |||
93507a263b | |||
1175461030 | |||
2f5e55bea0 | |||
c375e7b4df | |||
5d188c69ed | |||
6bff7751d0 | |||
1a5986abe0 | |||
cebac3c10e | |||
a5da3db966 | |||
cd30f75173 | |||
94df4ceb36 | |||
a14476c5fb | |||
d82bb29919 | |||
33af0c6a7c | |||
68f3f98bc3 | |||
f873cd5b1a | |||
fa8df286d9 | |||
76aa0c434a | |||
78dc0cfdf3 | |||
9654728bfb | |||
123d1864f4 | |||
96a91e988d | |||
19ecb1701e | |||
8085b2728d | |||
56cad31a36 | |||
3275bc4e93 | |||
8963378039 | |||
c7d435bb7a | |||
06cc7e4ea0 | |||
fba20e2cfb | |||
358f080c76 | |||
8cea57ff0f | |||
7b27f200b1 | |||
f4b207220c | |||
d835e1d14e | |||
75f92de8f8 | |||
293c1deee5 | |||
4dee05a967 | |||
8d6414d74a | |||
362ecdb583 | |||
058b9f9272 | |||
355762aa30 | |||
be4fb65470 | |||
4ed296bad4 | |||
a8aa862919 | |||
31ba1de53b | |||
bca1fef2b2 | |||
f33b31e048 | |||
0d10ebb7ca | |||
4a8abc948e | |||
6fd3672618 | |||
abd090bd48 | |||
bd9140f1f3 | |||
9d78b2d259 | |||
9c9528838a | |||
77b640b76b | |||
a8dfe98b1a | |||
cee41b87f7 | |||
7a95314e42 | |||
6aaf1f4f21 | |||
368a0ddd44 | |||
0808a10b7b | |||
6f052baa94 | |||
5bc67d3f6b | |||
74c6c5cfbc | |||
158b7fd166 | |||
c3f647dc96 | |||
5936c7b65c | |||
a8bcd85c93 | |||
127b558f95 | |||
6e9a27f40f | |||
4a13dbe3bb | |||
0f61f5ba03 | |||
5f1efbeb67 | |||
9c105914f0 | |||
579582740e | |||
b15544c163 | |||
94a63e3859 | |||
842b7e6c39 | |||
22f5011451 | |||
74120fe1f3 | |||
b4e8abd0ad | |||
336f1f4f50 | |||
1c256d8876 | |||
1ce0f0e7a5 | |||
fbf1901d86 | |||
0382f33c46 | |||
13372f3f99 | |||
e741cb7f0a | |||
fb289c6b17 | |||
75a7ea55d4 | |||
86573a5ccd | |||
6fe55a79f1 | |||
1a6cb9ee99 | |||
a495d9eca5 | |||
65a945f968 | |||
a9e8ed5087 | |||
00520b6a0e | |||
e6f2a3893a | |||
24c034ff6a | |||
631a93bcd8 | |||
b1763353ea | |||
f95bcf45ad | |||
15ec1abb6a | |||
cfda8dbb2b | |||
3aa2003951 | |||
0e473f4570 | |||
bfa824ee71 | |||
1157436a24 | |||
4596e78df0 | |||
813dfbd2d3 | |||
ba7dfb360c | |||
6aad750fe0 | |||
5e443ae347 | |||
c65f5f7728 | |||
74b62727af | |||
2e94562f79 | |||
061ea5648e | |||
439e7bbf4e | |||
0f1d51f866 | |||
75cfaf0672 | |||
ceaa732e5f | |||
deb2a2bd14 | |||
33853b6107 | |||
ab6e1b112b | |||
220e823c8d | |||
c666c3e251 | |||
9cc1773fa7 | |||
e78c7af715 | |||
b96e76134a | |||
fd2b206997 | |||
2d53c7c5b2 | |||
28ae5d710e | |||
7a13412ec7 | |||
c86610b917 | |||
502750492c | |||
8fc1653b0c | |||
6841ebc31d | |||
8757281467 | |||
20b1723e78 | |||
be78afeee5 | |||
3746a2566d | |||
0cb47cf7d7 | |||
dec81c4f27 | |||
54b335711a | |||
6bb8332b4b | |||
de9e304236 | |||
650af5eb64 | |||
47bdcb6050 | |||
58dc3e93d3 | |||
79b0a16f7a | |||
2b65ee433f | |||
001bbef9ee | |||
c4316e81e6 | |||
fac63541a4 | |||
dfd6cb29be | |||
5f75e531e6 | |||
a7648d60ce | |||
52c45c2d32 | |||
fd6755c93f | |||
a53ee2e35c | |||
4e6978ff6f | |||
91a5c4bdcb | |||
9a07ede615 | |||
dfab55112b | |||
1709b47bb7 | |||
2d10c246a8 | |||
6e2869834f | |||
b8b71c7dd2 | |||
c3f6c3dd82 | |||
844b245776 | |||
4bcf8e6975 | |||
0e52112016 | |||
32a9545360 | |||
9a44c92211 | |||
dd6aabf9ab | |||
91776311c7 | |||
882c82f82c | |||
43b9db6e45 | |||
032ceefa1d | |||
05fa266e6b | |||
56085310cb | |||
6a1d611fd1 | |||
c8a72c876d | |||
ec87a8ddfc | |||
33e34ebb83 | |||
dce435c882 | |||
396b3c3952 | |||
8cf42f4e15 | |||
17ea51ce27 | |||
04818ca626 | |||
6732b77594 | |||
3e4346e321 | |||
4dfc01899a | |||
b0b8ccfd4a | |||
4d35c66af3 | |||
509f7bd018 | |||
42b9b3d72a | |||
e280f9fe3e | |||
89493a2f1d | |||
e259bffca6 | |||
ba9164022d | |||
a4672ba00f | |||
c0bf267bae | |||
1a26a53659 | |||
8f4d7ac655 | |||
c5625d8d32 | |||
548a2a1d64 | |||
df01a58099 | |||
ac8ee9f981 | |||
b1805b70ea | |||
eae8a2914e | |||
03429db528 | |||
10f27250ee | |||
aed7963d11 | |||
2810413112 | |||
31fd92e071 | |||
b3b76d5d56 | |||
cd948dceae | |||
eb33a87ff5 | |||
bf560f4594 | |||
d77237ca5d | |||
211d596fdd | |||
f464b347b2 | |||
203c3ec233 | |||
e2a74dfc30 | |||
4e99da7b62 | |||
eb5ed50824 | |||
3b4539de79 | |||
3e4d1c04de | |||
fdf5748029 | |||
3562e94650 | |||
15c5bbcf22 | |||
c363423718 | |||
e58158c3cd | |||
ff2cd50bfa | |||
984692dc62 | |||
a7fc23dd96 | |||
c1bcbf8c63 | |||
9e69b8fe1b | |||
e0ae631d59 | |||
3b187b5246 | |||
20666763f0 | |||
f591c87665 | |||
e6fe701727 | |||
35a698ef72 | |||
998271414e | |||
1749f25420 | |||
4ab0e70a9a | |||
08989bde5e | |||
87cbff391c | |||
962923bbce | |||
311b081e60 | |||
4103948132 | |||
906f26698b | |||
8a1a583afe | |||
9e19b73ce6 | |||
e11706d99d | |||
301b811310 | |||
273cf3d565 | |||
f432cfd73a | |||
ffa756ccee | |||
e210a4b244 | |||
f1902a4471 | |||
04b865adae | |||
00df092a99 | |||
cad581388f | |||
e7ed3abb79 | |||
e5f6dc1b14 | |||
145da82cd8 | |||
64776d6bac | |||
64d123f524 | |||
0ec4ade683 | |||
4bfc445cf8 | |||
4232b1cedb | |||
474d77ac57 | |||
3c40355d7c | |||
cbc1aad58d | |||
89a30a47c5 | |||
881a5603dc | |||
68c48b2c8a | |||
0f9260869b | |||
412fdb0f7b | |||
81d52b6169 | |||
278d2169da | |||
5599b5a337 | |||
471d6d6031 | |||
b90d6b0f26 | |||
cd22da9c62 | |||
fb75c23f4e | |||
7f22994f68 | |||
7aba7b6064 | |||
e32030f364 | |||
b40619bcbd | |||
fe2e1d931f | |||
2e17e78052 | |||
f2fa82fd3c | |||
8cec4b60a6 | |||
bf9888099c | |||
02201631e7 | |||
8e80b4bfc1 | |||
63dfc0633f | |||
50f1847904 | |||
79840f0fca | |||
06bf7b0f31 | |||
dbb63b97bd | |||
3039cb5c02 | |||
b5d2570fe2 | |||
5f951e8f21 | |||
286f82cc99 | |||
ed5415aeb8 | |||
bef79df6bb | |||
2edabe0f99 | |||
91b5f0228d | |||
d639da741b | |||
84bd5ace6c | |||
63589d2ba9 | |||
d6f4ff26b5 | |||
b606a2e040 | |||
f685139d89 | |||
68d6ce60a9 | |||
2bba64fe3a | |||
774ed044fc | |||
9cc235cde0 | |||
91375e4c6d | |||
0ef8da64e0 | |||
9ef38171e2 | |||
455e4de6f3 | |||
4e319254dd | |||
04e8780dd0 | |||
0fe4384067 | |||
bdfcd0b99e | |||
5da87d1904 | |||
821edf0f51 | |||
04e822acfb | |||
c31fce3621 | |||
5c05cf2206 | |||
d4e544c62c | |||
d232248268 | |||
90025ed45d | |||
964151d9c7 | |||
409c9bf9d2 | |||
a135c06bcf | |||
efb1f4abf2 | |||
036a826b3a | |||
8d2b66a0fd | |||
31d6278b31 | |||
5558d7eef8 | |||
6103d86a47 | |||
b8899a534d | |||
fa6829a6a1 | |||
5335540c33 | |||
7f62de3854 | |||
0afa2e92d5 | |||
6d1b166ad7 | |||
4b6ddfb89b | |||
9ec260619b | |||
4c6ac9e4fe | |||
08fc3ea2e0 | |||
03e454b71d | |||
c527ea83fb | |||
8b9ac63657 | |||
0ba02f0830 | |||
1f6cef6f8a | |||
251942323e | |||
8e3efec40f | |||
3d0740f80e | |||
707a68cb4f | |||
e6b1a1fa76 | |||
3e8d450741 | |||
d0881b7420 | |||
27239b2dde | |||
3fbbaddece | |||
effe46db86 | |||
ba939c92ec | |||
e25cdd9d12 | |||
d394235ee0 | |||
349fc4143d | |||
4be9e6a0bc | |||
cb258146c4 | |||
9f039cef72 | |||
d08815bbc1 | |||
c04e38d011 | |||
794022d399 | |||
8e43190984 | |||
2212a6e40f | |||
63b69c0b0c | |||
278219b1d8 | |||
2ce4ce9064 | |||
4448418b63 | |||
9f1f37e780 | |||
40ccd1a469 | |||
a4ef0940ed | |||
2ff0aa09e3 | |||
32217a774f | |||
8856c8cd62 | |||
e843b8e188 | |||
c4f2e3a955 | |||
91301ec7fe | |||
13b03e7e50 | |||
8a0aa5a0c8 | |||
2bd8e7dca4 | |||
d75571ffa1 | |||
8683d529fc | |||
2ee4b6768d | |||
a9f0a85590 | |||
37160f973f | |||
9bbf50e864 | |||
51258ab28c | |||
29e5a213a5 | |||
2d261607df | |||
54144154f9 | |||
81daffe68e | |||
2ea20a8b29 | |||
2257c875f5 | |||
8a2e8ad953 | |||
fa5b1d9978 | |||
529fb07b42 | |||
4a261cac1a | |||
b3c8f9d508 | |||
1b878030aa | |||
46e403b20b | |||
1b1f728c58 | |||
309013efb3 | |||
705cf6499e | |||
f2ed4a92e2 | |||
0c8ca1b3c0 | |||
6ee5ee496e | |||
7f8aa2099c | |||
1d9797660b | |||
cd0d8a76c5 | |||
3f1251e78b | |||
89d4405563 | |||
8966364648 | |||
1c60e9b4fc | |||
843c860d98 | |||
6587e39992 | |||
757fa1410c | |||
dd6d8e0002 | |||
cd49406bfe | |||
7a3acc3249 | |||
db1c804812 | |||
493d58f358 | |||
fcd56dddc2 | |||
91b85f9919 | |||
4cecba8787 | |||
5930acc418 | |||
61d36c1723 | |||
c2a43c6f40 | |||
1ab00ca8b2 | |||
778baa6dbe | |||
25ab121e42 | |||
cf4949b4f5 | |||
4ca634d229 | |||
ca21b31696 | |||
38ff76d2b8 | |||
147bde5294 | |||
1a004f0c4d | |||
7d21bf15e8 | |||
a88ad8025b | |||
e06bf17d13 | |||
04a3669fc4 | |||
795075f90d | |||
2727df704f | |||
8ce8aadd9b | |||
2999e69f0f | |||
7ac16ed073 | |||
3585e4764b | |||
065e38c6aa | |||
9054ee18a1 | |||
9d8b95107d | |||
8731c86d0d | |||
d7fad4bd04 | |||
2746251dcd | |||
73e46e569f | |||
4f0f74d201 | |||
adf1e1982a | |||
f5405e835e | |||
85ee7fb3a4 | |||
ee00a5d8ee | |||
65d23fc9b9 | |||
3448d7cb70 | |||
db1abb02d6 | |||
36d78577e2 | |||
20832682ef | |||
8e6c592ad9 | |||
0b3115997a | |||
b07c5982e1 | |||
4a0b0d8735 | |||
7b9f67f4b4 | |||
577f17c916 | |||
d4c1d62781 | |||
80da1f1bb9 | |||
febdb85f96 | |||
96b76c8f5c | |||
411bf3be03 | |||
a98b6663e1 | |||
e7f35e6ca3 | |||
f0ec165d42 | |||
abd240468e | |||
b5e00027d1 | |||
1698554024 | |||
f4604bbf79 | |||
699ad316f0 | |||
fcbe233fdb | |||
4af8a9ed2d | |||
80627b4f89 | |||
83078cd49a | |||
53e0d13142 | |||
85901082a2 | |||
f19e27e875 | |||
c864046cee | |||
2d68235e55 | |||
2be8100e7c | |||
ab3e2562db | |||
123d6c72e4 | |||
0ea2135aa5 | |||
48e20cb5f7 | |||
0ffe0b6894 | |||
338156500b | |||
bfd9bd43c9 | |||
283e50e670 | |||
6fb5bb6a5e | |||
5d3bef32ca | |||
3ff26d5cfe | |||
88c1e504b0 | |||
0263677e1f | |||
940455f81c | |||
f541ea659c | |||
938cae1130 | |||
7f73e57c67 | |||
e50ec31351 | |||
a28fa219d7 | |||
8a1ba03bcb | |||
f8e7fb3d48 | |||
e8b7e70ec9 | |||
67c5aa0be9 | |||
e644380160 | |||
c5eb6fe6fb | |||
ae2ef324f2 | |||
d0337da8ea | |||
17b30b2ae2 | |||
5e17d53c7f | |||
22e0527502 | |||
ca3c6c5e8a | |||
4bd30f5e72 | |||
3cc26b15a1 | |||
9673dac22b | |||
0426149580 | |||
fce5c57548 | |||
4ee5264e24 | |||
101ca60b2b | |||
f28a0aa666 | |||
997bf91442 | |||
5a1de15332 | |||
42f8ec5b14 | |||
2fc1b99911 | |||
3a923060ce | |||
0985adfd74 | |||
59d628208b | |||
5c5699bba5 | |||
67e0214fa5 | |||
e17b6804a7 | |||
96e36f0604 | |||
a99858c64d | |||
ba50765c30 | |||
d7f6b36990 | |||
4439666e67 | |||
23febc6d94 | |||
fab4a7a602 | |||
092d2f8917 | |||
e9fb566c07 | |||
5a34e8fd7c | |||
fcfb2cfc3d | |||
e93b9560b5 | |||
8e4438b375 | |||
eaa5ce4467 | |||
c86c719e1a | |||
b30b88716e | |||
c3e6b1aa8a | |||
6d0ea13f97 | |||
dca4ea5cea | |||
be1e2c07ec | |||
2a0018e730 | |||
ac4497a1f2 | |||
d72c75db23 | |||
6821a45b7f | |||
f24cdd6564 | |||
cb69869836 | |||
0b85051a23 | |||
f2e2053134 | |||
b181f4bc30 | |||
f00dbdc215 | |||
309dcc82ca | |||
0922a7f410 | |||
7d91db607f | |||
2be583ad4d | |||
efcba698ac | |||
b191291737 | |||
c988b4d213 | |||
90007e2d9d | |||
29ef1e2c4b | |||
fbe84f9e47 | |||
b036d95bab | |||
bfffcb3910 | |||
3bc02b9662 | |||
bc450e4cee | |||
de02deac98 | |||
e2ad3b0183 | |||
caf6d02728 | |||
35f71f5793 | |||
b2a89ee4b9 | |||
b84d52be3d | |||
305973c0e7 | |||
7c251efc47 | |||
166f872712 | |||
8f2c485c92 | |||
613f2fc447 | |||
9a0d6124f3 | |||
2646ae29f0 | |||
b4707f46ad | |||
482e049ac7 | |||
c04adf7452 | |||
92e8aaf36f | |||
b1f486518e | |||
3cab0e69f1 | |||
1ec7878c07 | |||
cdd83c279c | |||
12cadf3af0 | |||
2bc6da038d | |||
3b4b34b369 | |||
6542f5f15a | |||
30b56dbcbd | |||
cbc8c1aed6 | |||
6afe59e76b | |||
88a93945d4 | |||
2847cefff7 | |||
96f16069a9 | |||
188c5aaa35 | |||
f5f59203a2 | |||
c329730de8 | |||
97a83c9b7a | |||
1e6df7eec0 | |||
f3c72f4f08 | |||
05cd30ac06 | |||
34adcec616 | |||
f9f46609ee | |||
b5bdfa6c2e | |||
ea4ef1655b | |||
7eb61a28be | |||
92b913ca37 | |||
3806f23b02 | |||
36045a8b0a | |||
943090db98 | |||
77caaca50d | |||
f66e0fa0e8 | |||
441075f610 | |||
b2878dca1d | |||
b883ce5c51 | |||
127dfadc6c | |||
ff9f3cb31f | |||
a21bd41580 | |||
8e9f1bcf18 | |||
4b06fc5323 | |||
cc27bb3231 | |||
c1700054f4 | |||
cc13310083 | |||
1012912272 | |||
edbd90a4e6 | |||
ddc3b5eb0b | |||
17bbfe8d89 | |||
96deb5b09d | |||
9b387d73e5 | |||
c02a6780b0 | |||
85ec9d95a4 | |||
6dfec4db40 | |||
2e9d7f5c3d | |||
696a711e39 | |||
887246a66b | |||
2993318d19 | |||
2b52cf01f5 | |||
cd9e0e0cc0 | |||
f66598db8a | |||
cf5c752dda | |||
a6d2c5e181 | |||
82760f4b91 | |||
62e099ace5 | |||
e92b7bd25e | |||
e7bb3b2776 | |||
ec3c882a44 | |||
66ce5a4a2d | |||
a110d445ac | |||
a67f3131e7 | |||
7f305aad1b | |||
80908efdcb | |||
e5d0097116 | |||
6d78ea5a45 | |||
550d6a6a9b | |||
9be8160bf2 | |||
3070565fa3 | |||
7194bb1b81 | |||
78cf0c73c8 | |||
4adc61bda9 | |||
93e0041d0e | |||
682a7fb6ba | |||
5f2398fe59 | |||
3abf466632 | |||
4bb429a0fb | |||
4a0e4fdb85 | |||
a43f882d48 | |||
9731e06728 | |||
8e764fc8fb | |||
4745ed2818 | |||
49de5d9b07 | |||
365cc198ba | |||
9899a0e098 | |||
5ea8cec16f | |||
9dd70ca9ae | |||
4fda35b466 | |||
31988f0529 | |||
db03846358 | |||
c6b3eccbdf | |||
3f259eb97a | |||
fac0b027b3 | |||
84b962f256 | |||
123ed5bd2e | |||
c3336251e0 | |||
c054d0f329 | |||
8dd68580a6 | |||
067484a6a8 | |||
c83e16aba4 | |||
d85fc456a9 | |||
dd2b324d8f | |||
0bce08d30f | |||
57d65177c8 | |||
040ee919e5 | |||
efa3fbff39 | |||
8c8eabf7ac | |||
3aea639fe4 | |||
046f1e6e58 | |||
37e8f495b4 | |||
512d7b07d0 | |||
527fa7ba9c | |||
288e7bc9c5 | |||
649b8ac7c6 | |||
96b2758169 | |||
40e0252d7d | |||
5171d99fe5 | |||
ea9d61c21c | |||
da25f3b84e | |||
1e34de98ab | |||
7b46f50cf1 | |||
cab40026f2 | |||
ac66bddeda | |||
f4237be9bd | |||
dd11ccb3fd | |||
c4549a5375 | |||
e9b3140d12 | |||
bdd2d57808 | |||
22f5128e39 | |||
ae1aa6f63e | |||
29a53d7e95 | |||
2fbb7be23b | |||
f7f39854f8 | |||
113528e1f2 | |||
6bffa06063 | |||
dcc41ef885 | |||
e909cc363d | |||
9a87ae575e | |||
860a7af679 | |||
4fdb21b414 | |||
eeb5297284 | |||
8de63b60b1 | |||
0b38d878a1 | |||
d29720fbce | |||
0556ae4749 | |||
1a86f72690 | |||
25d380f051 | |||
546d394868 | |||
8110040f86 | |||
239e4adf29 | |||
5662d41062 | |||
1e03ef484d | |||
1d95f10090 | |||
6768768373 | |||
dac801c8ac | |||
ece5ca52b2 | |||
3205d9e9da | |||
b931dc0f93 | |||
efe4e7df06 | |||
b94f70ea96 | |||
bd946c78f3 | |||
a507907443 | |||
0507b56bed | |||
ac405aa564 | |||
aad7c63282 | |||
1ff459d995 | |||
52454ca77b | |||
884f772362 | |||
bc06571dba | |||
1e8ad3d979 | |||
b0a469baab | |||
c968c1be78 | |||
a0bafa4952 | |||
e56f0db11b | |||
8bcbe07c87 | |||
ba9059c7c8 | |||
45783c1661 | |||
71777e7a6f | |||
c86ec0bd36 | |||
08b1b2669a | |||
94a655b055 | |||
717067e9eb | |||
5a5797d914 | |||
75826aca13 | |||
f52b7c030f | |||
c1386bcb04 | |||
234a9e48e9 | |||
908e6364c9 | |||
3ec511010f | |||
b3e1691c01 | |||
5adcc26190 | |||
db6dae7541 | |||
4e97f2a6fe | |||
4643046989 | |||
3c3ce71214 | |||
0682fe544a | |||
27e803e4a4 | |||
405c44c9e1 | |||
4c5f701b05 | |||
c7563a5f7c | |||
3ff3c335bc | |||
b9d5e1bbf1 | |||
e313b366a0 | |||
027c666265 | |||
4a3d94aaf9 | |||
c637e3657c | |||
9462dd3ddc | |||
c3524a9d57 | |||
53ff66c317 | |||
ec2ac2e80c | |||
27c72935f8 | |||
31c757d912 | |||
fcdc7a1a35 | |||
f13c776099 | |||
ebded1ec49 | |||
7b9d462fbc | |||
c174a1eb01 | |||
ac77997a7a | |||
fa352ff4d3 | |||
fd7745cbeb | |||
c348de96e4 | |||
1bb35b9204 | |||
cbbcc2d68b | |||
fcbf37f60f | |||
56ce7f5dcd | |||
0ccbc801cf | |||
4c52cc414f | |||
6b45386252 | |||
3acc761419 | |||
dd4c67b654 | |||
0c581cfb62 | |||
703bf9bb7a | |||
a40dee2230 | |||
cab9afa45f | |||
ba5946dc60 | |||
9bad83a551 | |||
a549828655 | |||
c1398a6a1a | |||
e11d1b5202 | |||
abdb846c3f | |||
b0b6dd8f87 | |||
4a971226e4 | |||
b10d4d3b8f | |||
6ed130fc16 | |||
90d4acd1a1 | |||
a42d2afcee | |||
ba020fbd21 | |||
af167e8e67 | |||
be82f4db9e | |||
6a97ac603a | |||
e59d3a339f | |||
db7cb12551 | |||
1049507b3f | |||
1303771b58 | |||
f6605bc3f8 | |||
80ec178d5f | |||
ef76bd355a | |||
b0a8bd7dfe | |||
5d8d7740ba | |||
3e64935844 | |||
7577115c3c | |||
2cb242e9b3 | |||
7aeccbb6b0 | |||
8406c7f431 | |||
8c98cc9458 | |||
715d7d4424 | |||
8e9eca6a97 | |||
5f15f51610 | |||
277de41200 | |||
63bce04648 | |||
76dfb7825a | |||
5cb0e75093 | |||
c730fd6e5f | |||
c2b97c3e3f | |||
e0ab8207ed | |||
ecad388846 | |||
9a120f43c8 | |||
4ff1306e0c | |||
1dd663af6e | |||
06d9821b2c | |||
abb20c65e3 | |||
9fb5674233 | |||
18ac109e5a | |||
14a0e85862 | |||
fa2b3bcc63 | |||
620555d210 | |||
ea3ceb382a | |||
37a30fbc3f | |||
99193a2d7b | |||
abfb99ba3f | |||
fcb311eecd | |||
54e4f88ada | |||
89125fde22 | |||
9f3eed6ca2 | |||
ab82358dcb | |||
75fddd0052 | |||
8a2c5f5b0d | |||
66caac0bbc | |||
ab8d897bd7 | |||
3024dbcf2c | |||
eeae3eca67 | |||
d0cda6d605 | |||
167e32a69f | |||
be2512bb4b | |||
c3f1c13a31 | |||
35cfb41a9c | |||
74cdf5350d | |||
9349232bd4 | |||
1811302deb | |||
e6564aa69f | |||
566a83b245 | |||
9eb9ddc668 | |||
cc5261051f | |||
fa870bc026 | |||
99d569ed0e | |||
a1a7b9c151 | |||
dc6340bf38 | |||
8732bea99b | |||
bbc6b71138 | |||
5fb096d7b9 | |||
cf8680f1ab | |||
115d8b5945 | |||
bd8b61651a | |||
d2ffaee9f8 | |||
8f61633551 | |||
d33d5b847d | |||
c8879df621 | |||
c367769781 | |||
02b44256b2 | |||
b6e722a048 | |||
7f2615b2a5 | |||
a232c2d509 | |||
c8205fda9f | |||
8df88e7fbb | |||
c8092269ba | |||
9d88f07955 | |||
f267375ac2 | |||
d44fa416ca | |||
41d7b27d43 | |||
5f461374b8 | |||
d5576779b7 | |||
c098be40ab | |||
333f7cc320 | |||
9180c751d8 | |||
131a04653f | |||
6e6d495bc7 | |||
2c07f758a0 | |||
251f0efec2 | |||
e8697068fb | |||
269c1bbf58 | |||
9b8493c304 | |||
c389a711ed | |||
382548e0a7 | |||
a90fc3d7fe | |||
48a6380e31 | |||
64ca96f470 | |||
59b3e30821 | |||
92e5e0e95b | |||
9202996c62 | |||
e22d54abd3 | |||
b53ba12fa2 | |||
335115041c | |||
bac67800be | |||
306df9e17f | |||
5f2e768376 | |||
575c92ec47 | |||
4d8bf0b621 | |||
954074942e | |||
341eb16a4c | |||
3a7eeff135 | |||
659f93b1de | |||
ea60e48d9d | |||
6054abaffb | |||
3a1feed723 | |||
85f3fc9944 | |||
87cf38a377 | |||
5e77b548b1 | |||
104de9e795 | |||
424f4a72ff | |||
ec6409914d | |||
6a25cacc3e | |||
b1af689546 | |||
0a5dfeb3d7 | |||
6a180f495f | |||
3672dbc5af | |||
81b50c0387 | |||
bd60c54911 | |||
87acaa0926 | |||
54d2c91320 | |||
94e7961df0 | |||
0d46ea5c71 | |||
6bd345b1ad | |||
cb384261b8 | |||
75301bec4b | |||
d00c320c00 | |||
e13ca94061 | |||
e02369ba6b | |||
82c35f2746 | |||
a9d935f9ef | |||
92f3154e8f | |||
eb06d0116f | |||
f66910d054 | |||
1b47132ebb | |||
a89cbf116d | |||
471ea680a5 | |||
51de84407f | |||
d252917792 | |||
dfa5f614aa | |||
f3a244e90a | |||
c7c83a35fa | |||
6025cd0ca5 | |||
ce05ce92bb | |||
a5fc640f2c | |||
0a4a3fece5 | |||
fcf728f3b5 | |||
cd4851c98b | |||
3db5f30403 | |||
58957122b9 | |||
8b46658b05 | |||
571e322d66 | |||
ddda02f092 | |||
bf6fa6bce4 | |||
025a3cf730 | |||
6d9eaee7f9 | |||
7471ce4530 | |||
149ffb844f | |||
4cf3ac42c8 | |||
d16c6b0e69 | |||
76e1aba58b | |||
c560d06b8d | |||
0681568d3e | |||
c7fdfb8116 | |||
c6b1a776dc | |||
8accb6f04e | |||
d1d055564c | |||
2741c58a01 | |||
64399dd8d6 | |||
f2ca11688e | |||
3c0c57359b | |||
062fc79286 | |||
2da565f5d4 | |||
e8373bbf65 | |||
26acc836d9 | |||
9c22af9685 | |||
75d0078a38 | |||
536b1a23fc | |||
cafff5e504 | |||
dd5c4b6864 | |||
a2c85a8531 | |||
de4d757650 | |||
f22cd0e8c7 | |||
702a1da0ac | |||
768007d980 | |||
2e40ab6244 | |||
95182ed74b | |||
8bc6cdf55c | |||
6c0b101fed | |||
9841c0a63d | |||
17d200dc88 | |||
c8fec556c0 | |||
b7f2959353 | |||
4899d3c458 | |||
c311dba465 | |||
75ec4256e2 | |||
bb5a91c179 | |||
8225f1ac92 | |||
06d16c6b13 | |||
90780818ca | |||
a160bd0062 | |||
7e0312493b | |||
ccfd06ad21 | |||
48c2146a42 | |||
abb9fa8cbd | |||
f99245b917 | |||
e35db82c27 | |||
6202525372 | |||
ec66c8fd3d | |||
19804c5718 | |||
fa5fa53592 | |||
da43ed8ce1 | |||
2d1f99b765 | |||
f01b8f29c6 | |||
440e9731e2 | |||
acdb54b88e | |||
00c3336ec8 | |||
a268ac7141 | |||
aedb513c9e | |||
ac3a7acc45 | |||
8409aa2571 | |||
199740cc61 | |||
a0f76cba62 | |||
5b8003cbe5 | |||
c0719102a0 | |||
9d8bbf34e6 | |||
ec9d0e70fb | |||
f1e44291cc | |||
b3b613d8b4 | |||
177b685557 | |||
71a4333f55 | |||
f1c21a912a | |||
84a142096f | |||
bb2b344d33 | |||
b96572774f | |||
641a96e4a9 | |||
4fe13c64a1 | |||
b3c2a56ece | |||
9c57b54a81 | |||
b131d3b2ec | |||
16bfafa29e | |||
f714adf6d2 | |||
39bd04f06f | |||
d755174bee | |||
1229e90817 | |||
5a638fa977 | |||
5e9d49a910 | |||
9a8599e4ba | |||
2d68308d49 | |||
0dd1c26cf3 | |||
881b2f2b38 | |||
6d1f9d4d02 | |||
da70122d9c | |||
6961a39cd2 |
16
.clang-format
Normal file
16
.clang-format
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
Language: Cpp
|
||||||
|
BasedOnStyle: Chromium
|
||||||
|
IndentWidth: 4
|
||||||
|
AlignConsecutiveMacros: false
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
BraceWrapping:
|
||||||
|
AfterFunction: true
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BreakConstructorInitializers: BeforeComma
|
||||||
|
ColumnLimit: 140
|
||||||
|
Cpp11BracedListStyle: false
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
open_collective: prismlauncher
|
20
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
20
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -1,6 +1,6 @@
|
|||||||
name: Bug Report
|
name: Bug Report
|
||||||
description: File a bug report
|
description: File a bug report
|
||||||
labels: [bug, needs-triage]
|
labels: [bug]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
@ -8,9 +8,9 @@ body:
|
|||||||
If you need help with running Minecraft, please visit us on our Discord before making a bug report.
|
If you need help with running Minecraft, please visit us on our Discord before making a bug report.
|
||||||
|
|
||||||
Before submitting a bug report, please make sure you have read this *entire* form, and that:
|
Before submitting a bug report, please make sure you have read this *entire* form, and that:
|
||||||
* You have read the [FAQ](https://github.com/PolyMC/PolyMC/wiki/FAQ) and it has not answered your question
|
* You have read the [Prism Launcher wiki](https://prismlauncher.org/wiki/) and it has not answered your question.
|
||||||
* Your bug is not caused by Minecraft or any mods you have installed.
|
* Your bug is not caused by Minecraft or any mods you have installed.
|
||||||
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/PolyMC/PolyMC/issues)
|
* Your issue has not been reported before, [make sure to use the search function!](https://github.com/PrismLauncher/PrismLauncher/issues)
|
||||||
|
|
||||||
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
|
**Do not forget to give your issue a descriptive title.** "Bug in the instance screen" makes it hard to distinguish issues at a glance.
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
@ -23,6 +23,20 @@ body:
|
|||||||
- macOS
|
- macOS
|
||||||
- Linux
|
- Linux
|
||||||
- Other
|
- Other
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Version of Prism Launcher
|
||||||
|
description: The version of Prism Launcher used in the bug report.
|
||||||
|
placeholder: Prism Launcher 5.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Version of Qt
|
||||||
|
description: The version of Qt used in the bug report. You can find it in Help -> About Prism Launcher -> About Qt.
|
||||||
|
placeholder: Qt 6.3.0
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Description of bug
|
label: Description of bug
|
||||||
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
blank_issues_enabled: true
|
blank_issues_enabled: true
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: PolyMC Matrix Support Room
|
- name: Prism Launcher Matrix Support Room
|
||||||
url: https://matrix.to/#/#support:polymc.org
|
url: https://matrix.to/#/#prism-support:matrix.org
|
||||||
about: Please ask for support here before opening an issue.
|
about: Please ask for support here before opening an issue.
|
||||||
|
9
.github/ISSUE_TEMPLATE/rfc.yml
vendored
9
.github/ISSUE_TEMPLATE/rfc.yml
vendored
@ -1,12 +1,12 @@
|
|||||||
# Template based on https://gitlab.archlinux.org/archlinux/rfcs/-/blob/0ba3b61e987e197f8d1901709409b8564958f78a/rfcs/0000-template.rst
|
# Template based on https://gitlab.archlinux.org/archlinux/rfcs/-/blob/0ba3b61e987e197f8d1901709409b8564958f78a/rfcs/0000-template.rst
|
||||||
name: Request for Comment (RFC)
|
name: Request for Comment (RFC)
|
||||||
description: Propose a larger change and start a discussion.
|
description: Propose a larger change and start a discussion.
|
||||||
labels: [RFC]
|
labels: [rfc]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
### Use this form to suggest a larger change for PolyMC.
|
### Use this form to suggest a larger change for Prism Launcher.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Goal
|
label: Goal
|
||||||
@ -18,11 +18,10 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Motivation
|
label: Motivation
|
||||||
description: |
|
description: |
|
||||||
Introduce the topic. If this is a not-well-known section of PolyMC, a detailed explanation of the background is recommended.
|
Introduce the topic. If this is a not-well-known section of Prism Launcher, a detailed explanation of the background is recommended.
|
||||||
Some example points of discussion:
|
Some example points of discussion:
|
||||||
- What specific problems are you facing right now that you're trying to address?
|
- What specific problems are you facing right now that you're trying to address?
|
||||||
- Are there any previous discussions? Link to them and summarize them (don't
|
- Are there any previous discussions? Link to them and summarize them (don't force your readers to read them though!).
|
||||||
- force your readers to read them though!).
|
|
||||||
- Is there any precedent set by other software? If so, link to resources.
|
- Is there any precedent set by other software? If so, link to resources.
|
||||||
placeholder: I don't like cats. I think many users also don't like cats.
|
placeholder: I don't like cats. I think many users also don't like cats.
|
||||||
validations:
|
validations:
|
||||||
|
10
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
10
.github/ISSUE_TEMPLATE/suggestion.yml
vendored
@ -1,29 +1,29 @@
|
|||||||
name: Suggestion
|
name: Suggestion
|
||||||
description: Make a suggestion
|
description: Make a suggestion
|
||||||
labels: [idea, needs-triage]
|
labels: [enhancement]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
### Use this form to suggest a feature for PolyMC.
|
### Use this form to suggest a feature for Prism Launcher.
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Role
|
label: Role
|
||||||
description: In what way do you use PolyMC that needs this feature?
|
description: In what way do you use Prism Launcher that needs this feature?
|
||||||
placeholder: I play modded Minecraft.
|
placeholder: I play modded Minecraft.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Suggestion
|
label: Suggestion
|
||||||
description: What do you want PolyMC to do?
|
description: What do you want Prism Launcher to do?
|
||||||
placeholder: I want the cat button to meow.
|
placeholder: I want the cat button to meow.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Benefit
|
label: Benefit
|
||||||
description: Why do you need PolyMC to do this?
|
description: Why do you need Prism Launcher to do this?
|
||||||
placeholder: so that I can always hear a cat when I need to.
|
placeholder: so that I can always hear a cat when I need to.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
3
.github/codeql/codeql-config.yml
vendored
Normal file
3
.github/codeql/codeql-config.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
query-filters:
|
||||||
|
- exclude:
|
||||||
|
id: cpp/fixme-comment
|
2
.github/dco.yml
vendored
Normal file
2
.github/dco.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
allowRemediationCommits:
|
||||||
|
individual: true
|
9
.github/pull_request_template.md
vendored
Normal file
9
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!--
|
||||||
|
Hey there! Thanks for your contribution.
|
||||||
|
|
||||||
|
Please make sure that your commits are signed off first.
|
||||||
|
If you don't know how that works, check out our contribution guidelines: https://github.com/PrismLauncher/PrismLauncher/blob/develop/CONTRIBUTING.md#signing-your-work
|
||||||
|
If you already created your commits, you can run `git rebase --signoff develop` to retroactively sign-off all your commits and `git push --force` to override what you have pushed already.
|
||||||
|
|
||||||
|
Note that signing and signing-off are two different things!
|
||||||
|
-->
|
2
.github/scripts/prepare_JREs.sh
vendored
2
.github/scripts/prepare_JREs.sh
vendored
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
URL_JDK8="https://api.adoptium.net/v3/binary/latest/8/ga/linux/x64/jre/hotspot/normal/eclipse"
|
URL_JDK8="https://api.adoptium.net/v3/binary/version/jdk8u312-b07/linux/x64/jre/hotspot/normal/eclipse"
|
||||||
URL_JDK17="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jre/hotspot/normal/eclipse"
|
URL_JDK17="https://api.adoptium.net/v3/binary/latest/17/ga/linux/x64/jre/hotspot/normal/eclipse"
|
||||||
|
|
||||||
mkdir -p JREs
|
mkdir -p JREs
|
||||||
|
661
.github/workflows/build.yml
vendored
661
.github/workflows/build.yml
vendored
@ -7,6 +7,23 @@ on:
|
|||||||
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
description: Type of build (Debug, Release, RelWithDebInfo, MinSizeRel)
|
||||||
type: string
|
type: string
|
||||||
default: Debug
|
default: Debug
|
||||||
|
is_qt_cached:
|
||||||
|
description: Enable Qt caching or not
|
||||||
|
type: string
|
||||||
|
default: true
|
||||||
|
secrets:
|
||||||
|
SPARKLE_ED25519_KEY:
|
||||||
|
description: Private key for signing Sparkle updates
|
||||||
|
required: false
|
||||||
|
WINDOWS_CODESIGN_CERT:
|
||||||
|
description: Certificate for signing Windows builds
|
||||||
|
required: false
|
||||||
|
WINDOWS_CODESIGN_PASSWORD:
|
||||||
|
description: Password for signing Windows builds
|
||||||
|
required: false
|
||||||
|
CACHIX_AUTH_TOKEN:
|
||||||
|
description: Private token for authenticating against Cachix cache
|
||||||
|
required: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -16,58 +33,153 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_version: 5.12.8
|
qt_ver: 5
|
||||||
qt_host: linux
|
|
||||||
|
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
qt_version: 5.15.2
|
qt_ver: 6
|
||||||
qt_host: linux
|
qt_host: linux
|
||||||
app_image: true
|
qt_arch: ''
|
||||||
|
qt_version: '6.2.4'
|
||||||
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
|
qt_tools: ''
|
||||||
|
|
||||||
- os: windows-2022
|
- os: windows-2022
|
||||||
qt_version: 5.15.2
|
name: "Windows-MinGW-w64"
|
||||||
qt_host: windows
|
msystem: clang64
|
||||||
qt_arch: win32_mingw81
|
vcvars_arch: 'amd64_x86'
|
||||||
|
|
||||||
- os: macos-11
|
- os: windows-2022
|
||||||
qt_version: 5.12.12
|
name: "Windows-MSVC-Legacy"
|
||||||
|
msystem: ''
|
||||||
|
architecture: 'win32'
|
||||||
|
vcvars_arch: 'amd64_x86'
|
||||||
|
qt_ver: 5
|
||||||
|
qt_host: windows
|
||||||
|
qt_arch: 'win32_msvc2019'
|
||||||
|
qt_version: '5.15.2'
|
||||||
|
qt_modules: ''
|
||||||
|
qt_tools: 'tools_openssl_x86'
|
||||||
|
|
||||||
|
- os: windows-2022
|
||||||
|
name: "Windows-MSVC"
|
||||||
|
msystem: ''
|
||||||
|
architecture: 'x64'
|
||||||
|
vcvars_arch: 'amd64'
|
||||||
|
qt_ver: 6
|
||||||
|
qt_host: windows
|
||||||
|
qt_arch: ''
|
||||||
|
qt_version: '6.5.1'
|
||||||
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
|
qt_tools: ''
|
||||||
|
|
||||||
|
- os: windows-2022
|
||||||
|
name: "Windows-MSVC-arm64"
|
||||||
|
msystem: ''
|
||||||
|
architecture: 'arm64'
|
||||||
|
vcvars_arch: 'amd64_arm64'
|
||||||
|
qt_ver: 6
|
||||||
|
qt_host: windows
|
||||||
|
qt_arch: 'win64_msvc2019_arm64'
|
||||||
|
qt_version: '6.5.1'
|
||||||
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
|
qt_tools: ''
|
||||||
|
|
||||||
|
- os: macos-12
|
||||||
|
name: macOS
|
||||||
|
macosx_deployment_target: 11.0
|
||||||
|
qt_ver: 6
|
||||||
qt_host: mac
|
qt_host: mac
|
||||||
macosx_deployment_target: 10.12
|
qt_arch: ''
|
||||||
|
qt_version: '6.5.0'
|
||||||
|
qt_modules: 'qt5compat qtimageformats'
|
||||||
|
qt_tools: ''
|
||||||
|
|
||||||
|
- os: macos-12
|
||||||
|
name: macOS-Legacy
|
||||||
|
macosx_deployment_target: 10.13
|
||||||
|
qt_ver: 5
|
||||||
|
qt_host: mac
|
||||||
|
qt_version: '5.15.2'
|
||||||
|
qt_modules: ''
|
||||||
|
qt_tools: ''
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
env:
|
env:
|
||||||
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
|
MACOSX_DEPLOYMENT_TARGET: ${{ matrix.macosx_deployment_target }}
|
||||||
INSTALL_DIR: "install"
|
INSTALL_DIR: "install"
|
||||||
|
INSTALL_PORTABLE_DIR: "install-portable"
|
||||||
|
INSTALL_APPIMAGE_DIR: "install-appdir"
|
||||||
BUILD_DIR: "build"
|
BUILD_DIR: "build"
|
||||||
|
CCACHE_VAR: ""
|
||||||
|
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install 32bit mingw on Windows
|
##
|
||||||
if: runner.os == 'Windows'
|
# PREPARE
|
||||||
uses: egor-tensin/setup-mingw@v2
|
##
|
||||||
with:
|
|
||||||
platform: x86
|
|
||||||
|
|
||||||
- name: Install 32bit zlib via Strawberry on Windows
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
run: |
|
|
||||||
choco install strawberryperl -y --force --x86
|
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: 'true'
|
submodules: 'true'
|
||||||
|
|
||||||
# We need to do this here because it inexplicably fails if we split the step
|
- name: 'Setup MSYS2'
|
||||||
- name: Download and install OpenSSL libs on Windows
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
if: runner.os == 'Windows'
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: ${{ matrix.msystem }}
|
||||||
|
update: true
|
||||||
|
install: >-
|
||||||
|
git
|
||||||
|
mingw-w64-x86_64-binutils
|
||||||
|
pacboy: >-
|
||||||
|
toolchain:p
|
||||||
|
cmake:p
|
||||||
|
extra-cmake-modules:p
|
||||||
|
ninja:p
|
||||||
|
qt6-base:p
|
||||||
|
qt6-svg:p
|
||||||
|
qt6-imageformats:p
|
||||||
|
quazip-qt6:p
|
||||||
|
ccache:p
|
||||||
|
qt6-5compat:p
|
||||||
|
cmark:p
|
||||||
|
|
||||||
|
- name: Force newer ccache
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == '' && inputs.build_type == 'Debug'
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
choco install ccache --version 4.7.1
|
||||||
python -m pip install aqtinstall==2.0.5
|
|
||||||
python -m aqt install-tool -O "${{ github.workspace }}\Qt\" windows desktop tools_openssl_x86
|
- name: Setup ccache
|
||||||
mkdir ${{ env.INSTALL_DIR }}
|
if: (runner.os != 'Windows' || matrix.msystem == '') && inputs.build_type == 'Debug'
|
||||||
copy "${{ github.workspace }}\Qt\Tools\OpenSSL\Win_x86\bin\libssl-1_1.dll" "${{ github.workspace }}\${{ env.INSTALL_DIR }}\"
|
uses: hendrikmuhs/ccache-action@v1.2.9
|
||||||
copy "${{ github.workspace }}\Qt\Tools\OpenSSL\Win_x86\bin\libcrypto-1_1.dll" "${{ github.workspace }}\${{ env.INSTALL_DIR }}\"
|
with:
|
||||||
|
key: ${{ matrix.os }}-qt${{ matrix.qt_ver }}-${{ matrix.architecture }}
|
||||||
|
|
||||||
|
- name: Retrieve ccache cache (Windows MinGW-w64)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||||
|
uses: actions/cache@v3.3.1
|
||||||
|
with:
|
||||||
|
path: '${{ github.workspace }}\.ccache'
|
||||||
|
key: ${{ matrix.os }}-mingw-w64-ccache-${{ github.run_id }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ matrix.os }}-mingw-w64-ccache
|
||||||
|
|
||||||
|
- name: Setup ccache (Windows MinGW-w64)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != '' && inputs.build_type == 'Debug'
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
ccache --set-config=cache_dir='${{ github.workspace }}\.ccache'
|
||||||
|
ccache --set-config=max_size='500M'
|
||||||
|
ccache --set-config=compression=true
|
||||||
|
ccache -p # Show config
|
||||||
|
ccache -z # Zero stats
|
||||||
|
|
||||||
|
- name: Use ccache on Debug builds only
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "CCACHE_VAR=ccache" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set short version
|
- name: Set short version
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -75,148 +187,429 @@ jobs:
|
|||||||
ver_short=`git rev-parse --short HEAD`
|
ver_short=`git rev-parse --short HEAD`
|
||||||
echo "VERSION=$ver_short" >> $GITHUB_ENV
|
echo "VERSION=$ver_short" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install OpenJDK
|
- name: Install Dependencies (Linux)
|
||||||
uses: AdoptOpenJDK/install-jdk@v1
|
if: runner.os == 'Linux'
|
||||||
with:
|
|
||||||
version: '17'
|
|
||||||
|
|
||||||
- name: Cache Qt
|
|
||||||
id: cache-qt
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: "${{ github.workspace }}/Qt/"
|
|
||||||
key: ${{ runner.os }}-${{ matrix.qt_version }}-${{ matrix.qt_arch }}-qt_cache
|
|
||||||
|
|
||||||
- name: Install Qt
|
|
||||||
if: runner.os != 'Linux' || matrix.app_image == true
|
|
||||||
uses: jurplel/install-qt-action@v2
|
|
||||||
with:
|
|
||||||
version: ${{ matrix.qt_version }}
|
|
||||||
host: ${{ matrix.qt_host }}
|
|
||||||
arch: ${{ matrix.qt_arch }}
|
|
||||||
cached: ${{ steps.cache-qt.outputs.cache-hit }}
|
|
||||||
dir: "${{ github.workspace }}/Qt/"
|
|
||||||
|
|
||||||
- name: Install System Qt on Linux
|
|
||||||
if: runner.os == 'Linux' && matrix.app_image != true
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -y update
|
sudo apt-get -y update
|
||||||
|
sudo apt-get -y install ninja-build extra-cmake-modules scdoc
|
||||||
|
|
||||||
|
- name: Install Dependencies (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: |
|
||||||
|
brew update
|
||||||
|
brew install ninja extra-cmake-modules
|
||||||
|
|
||||||
|
- name: Install Qt (Linux)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
|
run: |
|
||||||
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
sudo apt-get -y install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||||
|
|
||||||
- name: Install Ninja
|
- name: Install host Qt (Windows MSVC arm64)
|
||||||
uses: urkle/action-get-ninja@v1
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
aqtversion: '==3.1.*'
|
||||||
|
py7zrversion: '>=0.20.2'
|
||||||
|
version: ${{ matrix.qt_version }}
|
||||||
|
host: 'windows'
|
||||||
|
target: 'desktop'
|
||||||
|
arch: ''
|
||||||
|
modules: ${{ matrix.qt_modules }}
|
||||||
|
tools: ${{ matrix.qt_tools }}
|
||||||
|
cache: ${{ inputs.is_qt_cached }}
|
||||||
|
cache-key-prefix: host-qt-arm64-windows
|
||||||
|
dir: ${{ github.workspace }}\HostQt
|
||||||
|
set-env: false
|
||||||
|
|
||||||
- name: Download linuxdeploy family for AppImage on Linux
|
- name: Install Qt (macOS, Linux, Qt 6 & Windows MSVC)
|
||||||
if: matrix.app_image == true
|
if: runner.os == 'Linux' && matrix.qt_ver == 6 || runner.os == 'macOS' || (runner.os == 'Windows' && matrix.msystem == '')
|
||||||
|
uses: jurplel/install-qt-action@v3
|
||||||
|
with:
|
||||||
|
aqtversion: '==3.1.*'
|
||||||
|
py7zrversion: '>=0.20.2'
|
||||||
|
version: ${{ matrix.qt_version }}
|
||||||
|
host: ${{ matrix.qt_host }}
|
||||||
|
target: 'desktop'
|
||||||
|
arch: ${{ matrix.qt_arch }}
|
||||||
|
modules: ${{ matrix.qt_modules }}
|
||||||
|
tools: ${{ matrix.qt_tools }}
|
||||||
|
cache: ${{ inputs.is_qt_cached }}
|
||||||
|
|
||||||
|
- name: Install MSVC (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' # We want this for MinGW builds as well, as we need SignTool
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
with:
|
||||||
|
vsversion: 2022
|
||||||
|
arch: ${{ matrix.vcvars_arch }}
|
||||||
|
|
||||||
|
- name: Prepare AppImage (Linux)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
run: |
|
run: |
|
||||||
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
|
||||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage"
|
||||||
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
wget "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage"
|
||||||
|
|
||||||
- name: Download JREs for AppImage on Linux
|
|
||||||
if: matrix.app_image == true
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
${{ github.workspace }}/.github/scripts/prepare_JREs.sh
|
||||||
|
|
||||||
- name: Configure CMake
|
- name: Add QT_HOST_PATH var (Windows MSVC arm64)
|
||||||
if: runner.os != 'Linux'
|
if: runner.os == 'Windows' && matrix.architecture == 'arm64'
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -G Ninja
|
echo "QT_HOST_PATH=${{ github.workspace }}\HostQt\Qt\${{ matrix.qt_version }}\msvc2019_64" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
- name: Configure CMake on Linux
|
##
|
||||||
|
# CONFIGURE
|
||||||
|
##
|
||||||
|
|
||||||
|
- name: Configure CMake (macOS)
|
||||||
|
if: runner.os == 'macOS' && matrix.qt_ver == 6
|
||||||
|
run: |
|
||||||
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -G Ninja
|
||||||
|
|
||||||
|
- name: Configure CMake (macOS-Legacy)
|
||||||
|
if: runner.os == 'macOS' && matrix.qt_ver == 5
|
||||||
|
run: |
|
||||||
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DMACOSX_SPARKLE_UPDATE_PUBLIC_KEY="" -DMACOSX_SPARKLE_UPDATE_FEED_URL="" -G Ninja
|
||||||
|
|
||||||
|
- name: Configure CMake (Windows MinGW-w64)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=6 -DCMAKE_OBJDUMP=/mingw64/bin/objdump.exe -G Ninja
|
||||||
|
|
||||||
|
- name: Configure CMake (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
|
run: |
|
||||||
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=${{ env.INSTALL_DIR }} -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=${{ matrix.name }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -DCMAKE_MSVC_RUNTIME_LIBRARY="MultiThreadedDLL" -A${{ matrix.architecture}} -DLauncher_FORCE_BUNDLED_LIBS=ON
|
||||||
|
# https://github.com/ccache/ccache/wiki/MS-Visual-Studio (I coudn't figure out the compiler prefix)
|
||||||
|
if ("${{ env.CCACHE_VAR }}")
|
||||||
|
{
|
||||||
|
Copy-Item C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/ccache.exe -Destination C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/cl.exe
|
||||||
|
echo "CLToolExe=cl.exe" >> $env:GITHUB_ENV
|
||||||
|
echo "CLToolPath=C:/ProgramData/chocolatey/lib/ccache/tools/ccache-4.7.1-windows-x86_64/" >> $env:GITHUB_ENV
|
||||||
|
echo "TrackFileAccess=false" >> $env:GITHUB_ENV
|
||||||
|
}
|
||||||
|
# Needed for ccache, but also speeds up compile
|
||||||
|
echo "UseMultiToolTask=true" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Configure CMake (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DLauncher_PORTABLE=OFF -G Ninja
|
cmake -S . -B ${{ env.BUILD_DIR }} -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} -DENABLE_LTO=ON -DLauncher_BUILD_PLATFORM=Linux -DCMAKE_C_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ env.CCACHE_VAR }} -DLauncher_QT_VERSION_MAJOR=${{ matrix.qt_ver }} -G Ninja
|
||||||
|
|
||||||
|
##
|
||||||
|
# BUILD
|
||||||
|
##
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
|
if: runner.os != 'Windows'
|
||||||
run: |
|
run: |
|
||||||
cmake --build ${{ env.BUILD_DIR }}
|
cmake --build ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
- name: Install
|
- name: Build (Windows MinGW-w64)
|
||||||
if: runner.os != 'Linux'
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
cmake --build ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
|
- name: Build (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
|
run: |
|
||||||
|
cmake --build ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
||||||
|
|
||||||
|
##
|
||||||
|
# TEST
|
||||||
|
##
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
if: runner.os != 'Windows'
|
||||||
|
run: |
|
||||||
|
ctest -E "^example64|example$" --test-dir build --output-on-failure
|
||||||
|
|
||||||
|
- name: Test (Windows MinGW-w64)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
ctest -E "^example64|example$" --test-dir build --output-on-failure
|
||||||
|
|
||||||
|
- name: Test (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == '' && matrix.architecture != 'arm64'
|
||||||
|
run: |
|
||||||
|
ctest -E "^example64|example$" --test-dir build --output-on-failure -C ${{ inputs.build_type }}
|
||||||
|
|
||||||
|
##
|
||||||
|
# PACKAGE BUILDS
|
||||||
|
##
|
||||||
|
|
||||||
|
- name: Package (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
cmake --install ${{ env.BUILD_DIR }}
|
cmake --install ${{ env.BUILD_DIR }}
|
||||||
|
|
||||||
- name: Install on Linux
|
cd ${{ env.INSTALL_DIR }}
|
||||||
|
chmod +x "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||||
|
sudo codesign --sign - --deep --force --entitlements "../program_info/App.entitlements" --options runtime "PrismLauncher.app/Contents/MacOS/prismlauncher"
|
||||||
|
mv "PrismLauncher.app" "Prism Launcher.app"
|
||||||
|
tar -czf ../PrismLauncher.tar.gz *
|
||||||
|
|
||||||
|
- name: Make Sparkle signature (macOS)
|
||||||
|
if: matrix.name == 'macOS'
|
||||||
|
run: |
|
||||||
|
if [ '${{ secrets.SPARKLE_ED25519_KEY }}' != '' ]; then
|
||||||
|
brew install openssl@3
|
||||||
|
echo '${{ secrets.SPARKLE_ED25519_KEY }}' > ed25519-priv.pem
|
||||||
|
signature=$(/usr/local/opt/openssl@3/bin/openssl pkeyutl -sign -rawin -in ${{ github.workspace }}/PrismLauncher.tar.gz -inkey ed25519-priv.pem | openssl base64 | tr -d \\n)
|
||||||
|
rm ed25519-priv.pem
|
||||||
|
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||||
|
### Artifact Information :information_source:
|
||||||
|
- :memo: Sparkle Signature (ed25519): \`$signature\`
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat >> $GITHUB_STEP_SUMMARY << EOF
|
||||||
|
### Artifact Information :information_source:
|
||||||
|
- :warning: Sparkle Signature (ed25519): No private key available (likely a pull request or fork)
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Package (Windows MinGW-w64)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
cmake --install ${{ env.BUILD_DIR }}
|
||||||
|
touch ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
|
- name: Package (Windows MSVC)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
|
run: |
|
||||||
|
cmake --install ${{ env.BUILD_DIR }} --config ${{ inputs.build_type }}
|
||||||
|
|
||||||
|
cd ${{ env.INSTALL_DIR }}
|
||||||
|
if ("${{ matrix.qt_ver }}" -eq "5")
|
||||||
|
{
|
||||||
|
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libcrypto-1_1.dll -Destination libcrypto-1_1.dll
|
||||||
|
Copy-Item D:/a/PrismLauncher/Qt/Tools/OpenSSL/Win_x86/bin/libssl-1_1.dll -Destination libssl-1_1.dll
|
||||||
|
}
|
||||||
|
cd ${{ github.workspace }}
|
||||||
|
|
||||||
|
Get-ChildItem ${{ env.INSTALL_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
|
|
||||||
|
- name: Fetch codesign certificate (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: bash # yes, we are not using MSYS2 or PowerShell here
|
||||||
|
run: |
|
||||||
|
echo '${{ secrets.WINDOWS_CODESIGN_CERT }}' | base64 --decode > codesign.pfx
|
||||||
|
|
||||||
|
- name: Sign executable (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
if (Get-Content ./codesign.pfx){
|
||||||
|
cd ${{ env.INSTALL_DIR }}
|
||||||
|
# We ship the exact same executable for portable and non-portable editions, so signing just once is fine
|
||||||
|
SignTool sign /fd sha256 /td sha256 /f ../codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com prismlauncher.exe prismlauncher_filelink.exe
|
||||||
|
} else {
|
||||||
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Package (Windows MinGW-w64, portable)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||||
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
|
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=$(cygpath -u $l); l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done >> ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||||
|
|
||||||
|
- name: Package (Windows MSVC, portable)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem == ''
|
||||||
|
run: |
|
||||||
|
cp -r ${{ env.INSTALL_DIR }} ${{ env.INSTALL_PORTABLE_DIR }} # cmake install on Windows is slow, let's just copy instead
|
||||||
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
|
|
||||||
|
Get-ChildItem ${{ env.INSTALL_PORTABLE_DIR }} -Recurse | ForEach FullName | Resolve-Path -Relative | %{ $_.TrimStart('.\') } | %{ $_.TrimStart('${{ env.INSTALL_PORTABLE_DIR }}') } | %{ $_.TrimStart('\') } | Out-File -FilePath ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
|
- name: Package (Windows, installer)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
cd ${{ env.INSTALL_DIR }}
|
||||||
|
makensis -NOCD "${{ github.workspace }}/${{ env.BUILD_DIR }}/program_info/win_install.nsi"
|
||||||
|
|
||||||
|
- name: Sign installer (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
if (Get-Content ./codesign.pfx){
|
||||||
|
SignTool sign /fd sha256 /td sha256 /f codesign.pfx /p '${{ secrets.WINDOWS_CODESIGN_PASSWORD }}' /tr http://timestamp.digicert.com PrismLauncher-Setup.exe
|
||||||
|
} else {
|
||||||
|
":warning: Skipped code signing for Windows, as certificate was not present." >> $env:GITHUB_STEP_SUMMARY
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Package (Linux)
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: |
|
run: |
|
||||||
DESTDIR=${{ env.INSTALL_DIR }} cmake --install ${{ env.BUILD_DIR }}
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_DIR }}
|
||||||
|
for l in $(find ${{ env.INSTALL_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_DIR }}/manifest.txt
|
||||||
|
|
||||||
- name: Bundle AppImage
|
cd ${{ env.INSTALL_DIR }}
|
||||||
if: matrix.app_image == true
|
tar --owner root --group root -czf ../PrismLauncher.tar.gz *
|
||||||
|
|
||||||
|
- name: Package (Linux, portable)
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
run: |
|
||||||
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_PORTABLE_DIR }} --component portable
|
||||||
|
for l in $(find ${{ env.INSTALL_PORTABLE_DIR }} -type f); do l=${l#$(pwd)/}; l=${l#${{ env.INSTALL_PORTABLE_DIR }}/}; l=${l#./}; echo $l; done > ${{ env.INSTALL_PORTABLE_DIR }}/manifest.txt
|
||||||
|
|
||||||
|
|
||||||
|
cd ${{ env.INSTALL_PORTABLE_DIR }}
|
||||||
|
tar -czf ../PrismLauncher-portable.tar.gz *
|
||||||
|
|
||||||
|
- name: Package AppImage (Linux)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
export OUTPUT="PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
cmake --install ${{ env.BUILD_DIR }} --prefix ${{ env.INSTALL_APPIMAGE_DIR }}/usr
|
||||||
|
|
||||||
|
export OUTPUT="PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage"
|
||||||
|
|
||||||
chmod +x linuxdeploy-*.AppImage
|
chmod +x linuxdeploy-*.AppImage
|
||||||
|
|
||||||
mkdir -p ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
|
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-{8,17}-openjdk
|
||||||
|
mkdir -p ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk
|
cp -r ${{ github.workspace }}/JREs/jre8/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk
|
||||||
|
|
||||||
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk
|
cp -r ${{ github.workspace }}/JREs/jre17/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk
|
||||||
|
|
||||||
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib"
|
cp -r /home/runner/work/PrismLauncher/Qt/${{ matrix.qt_version }}/gcc_64/plugins/iconengines/* ${{ env.INSTALL_APPIMAGE_DIR }}/usr/plugins/iconengines
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server"
|
|
||||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
|
||||||
|
|
||||||
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_DIR }}/usr/share/icons/hicolor/scalable/apps/org.polymc.PolyMC.svg
|
cp /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/
|
||||||
|
cp /usr/lib/x86_64-linux-gnu/libssl.so.1.1 ${{ env.INSTALL_APPIMAGE_DIR }}//usr/lib/
|
||||||
|
|
||||||
- name: Run windeployqt
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib"
|
||||||
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64/server"
|
||||||
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-8-openjdk/lib/amd64"
|
||||||
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib/server"
|
||||||
|
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${{ env.INSTALL_APPIMAGE_DIR }}/usr/lib/jvm/java-17-openjdk/lib"
|
||||||
|
export LD_LIBRARY_PATH
|
||||||
|
|
||||||
|
./linuxdeploy-x86_64.AppImage --appdir ${{ env.INSTALL_APPIMAGE_DIR }} --output appimage --plugin qt -i ${{ env.INSTALL_APPIMAGE_DIR }}/usr/share/icons/hicolor/scalable/apps/org.prismlauncher.PrismLauncher.svg
|
||||||
|
|
||||||
|
##
|
||||||
|
# UPLOAD BUILDS
|
||||||
|
##
|
||||||
|
|
||||||
|
- name: Upload binary tarball (macOS)
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: PrismLauncher.tar.gz
|
||||||
|
|
||||||
|
- name: Upload binary zip (Windows)
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
run: |
|
uses: actions/upload-artifact@v3
|
||||||
windeployqt --no-translations --no-system-d3d-compiler --no-opengl-sw "${{ env.INSTALL_DIR }}/polymc.exe"
|
|
||||||
|
|
||||||
- name: Run macdeployqt
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: |
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
|
||||||
macdeployqt "PolyMC.app" -executable="PolyMC.app/Contents/MacOS/polymc" -always-overwrite
|
|
||||||
|
|
||||||
- name: chmod binary on macOS
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: |
|
|
||||||
chmod +x "${{ github.workspace }}/${{ env.INSTALL_DIR }}/PolyMC.app/Contents/MacOS/polymc"
|
|
||||||
|
|
||||||
- name: tar bundle on macOS
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: |
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
|
||||||
tar -czf ../PolyMC.tar.gz *
|
|
||||||
|
|
||||||
- name: tar on Linux
|
|
||||||
if: runner.os == 'Linux' && matrix.app_image != true
|
|
||||||
run: |
|
|
||||||
cd ${{ env.INSTALL_DIR }}
|
|
||||||
tar -czf ../PolyMC.tar.gz *
|
|
||||||
|
|
||||||
- name: Upload Linux tar.gz
|
|
||||||
if: runner.os == 'Linux' && matrix.app_image != true
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PolyMC.tar.gz
|
|
||||||
|
|
||||||
- name: Upload AppImage for Linux
|
|
||||||
if: matrix.app_image == true
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
|
||||||
path: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
|
||||||
|
|
||||||
- name: Upload package for Windows
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
|
||||||
path: ${{ env.INSTALL_DIR }}/**
|
path: ${{ env.INSTALL_DIR }}/**
|
||||||
|
|
||||||
- name: Upload package for macOS
|
- name: Upload binary zip (Windows, portable)
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: PolyMC-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
name: PrismLauncher-${{ matrix.name }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
path: PolyMC.tar.gz
|
path: ${{ env.INSTALL_PORTABLE_DIR }}/**
|
||||||
|
|
||||||
|
- name: Upload installer (Windows)
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ matrix.name }}-Setup-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: PrismLauncher-Setup.exe
|
||||||
|
|
||||||
|
- name: Upload binary tarball (Linux, Qt 5)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: PrismLauncher.tar.gz
|
||||||
|
|
||||||
|
- name: Upload binary tarball (Linux, portable, Qt 5)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver != 6
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ runner.os }}-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: PrismLauncher-portable.tar.gz
|
||||||
|
|
||||||
|
- name: Upload binary tarball (Linux, Qt 6)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver !=5
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ runner.os }}-Qt6-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: PrismLauncher.tar.gz
|
||||||
|
|
||||||
|
- name: Upload binary tarball (Linux, portable, Qt 6)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ runner.os }}-Qt6-Portable-${{ env.VERSION }}-${{ inputs.build_type }}
|
||||||
|
path: PrismLauncher-portable.tar.gz
|
||||||
|
|
||||||
|
- name: Upload AppImage (Linux)
|
||||||
|
if: runner.os == 'Linux' && matrix.qt_ver != 5
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
|
path: PrismLauncher-${{ runner.os }}-${{ env.VERSION }}-${{ inputs.build_type }}-x86_64.AppImage
|
||||||
|
|
||||||
|
- name: ccache stats (Windows MinGW-w64)
|
||||||
|
if: runner.os == 'Windows' && matrix.msystem != ''
|
||||||
|
shell: msys2 {0}
|
||||||
|
run: |
|
||||||
|
ccache -s
|
||||||
|
|
||||||
|
flatpak:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: bilelmoussaoui/flatpak-github-actions:kde-5.15-22.08
|
||||||
|
options: --privileged
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
- name: Build Flatpak (Linux)
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||||
|
with:
|
||||||
|
bundle: "Prism Launcher.flatpak"
|
||||||
|
manifest-path: flatpak/org.prismlauncher.PrismLauncher.yml
|
||||||
|
|
||||||
|
nix:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
package:
|
||||||
|
- prismlauncher
|
||||||
|
- prismlauncher-qt5
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
- name: Install nix
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
uses: cachix/install-nix-action@v21
|
||||||
|
with:
|
||||||
|
install_url: https://nixos.org/nix/install
|
||||||
|
extra_nix_config: |
|
||||||
|
auto-optimise-store = true
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
- uses: cachix/cachix-action@v12
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
with:
|
||||||
|
name: prismlauncher
|
||||||
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
- name: Build
|
||||||
|
if: inputs.build_type == 'Debug'
|
||||||
|
run: nix build .#${{ matrix.package }} --print-build-logs
|
||||||
|
35
.github/workflows/codeql.yml
vendored
Normal file
35
.github/workflows/codeql.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
name: "CodeQL Code Scanning"
|
||||||
|
|
||||||
|
on: [ push, pull_request, workflow_dispatch ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
CodeQL:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v2
|
||||||
|
with:
|
||||||
|
config-file: ./.github/codeql/codeql-config.yml
|
||||||
|
queries: security-and-quality
|
||||||
|
languages: cpp, java
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run:
|
||||||
|
sudo apt-get -y update
|
||||||
|
|
||||||
|
sudo apt-get -y install ninja-build extra-cmake-modules scdoc qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5
|
||||||
|
|
||||||
|
- name: Configure and Build
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr -DLauncher_QT_VERSION_MAJOR=5 -G Ninja
|
||||||
|
|
||||||
|
cmake --build build
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v2
|
22
.github/workflows/trigger_builds.yml
vendored
22
.github/workflows/trigger_builds.yml
vendored
@ -3,18 +3,22 @@ name: Build Application
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore:
|
branches-ignore:
|
||||||
- 'stable'
|
- 'renovate/**'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
- '**/LICENSE'
|
- '**/LICENSE'
|
||||||
- 'flake.lock'
|
- 'flake.lock'
|
||||||
- '**.nix'
|
- 'packages/**'
|
||||||
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
|
- '.markdownlint**'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
- '**/LICENSE'
|
- '**/LICENSE'
|
||||||
- 'flake.lock'
|
- 'flake.lock'
|
||||||
- '**.nix'
|
- 'packages/**'
|
||||||
|
- '.github/ISSUE_TEMPLATE/**'
|
||||||
|
- '.markdownlint**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -24,9 +28,9 @@ jobs:
|
|||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
with:
|
with:
|
||||||
build_type: Debug
|
build_type: Debug
|
||||||
|
is_qt_cached: true
|
||||||
build_release:
|
secrets:
|
||||||
name: Build Release
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
uses: ./.github/workflows/build.yml
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
with:
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
build_type: Release
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
139
.github/workflows/trigger_release.yml
vendored
139
.github/workflows/trigger_release.yml
vendored
@ -12,6 +12,12 @@ jobs:
|
|||||||
uses: ./.github/workflows/build.yml
|
uses: ./.github/workflows/build.yml
|
||||||
with:
|
with:
|
||||||
build_type: Release
|
build_type: Release
|
||||||
|
is_qt_cached: false
|
||||||
|
secrets:
|
||||||
|
SPARKLE_ED25519_KEY: ${{ secrets.SPARKLE_ED25519_KEY }}
|
||||||
|
WINDOWS_CODESIGN_CERT: ${{ secrets.WINDOWS_CODESIGN_CERT }}
|
||||||
|
WINDOWS_CODESIGN_PASSWORD: ${{ secrets.WINDOWS_CODESIGN_PASSWORD }}
|
||||||
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
|
||||||
create_release:
|
create_release:
|
||||||
needs: build_release
|
needs: build_release
|
||||||
@ -19,10 +25,56 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: 'true'
|
||||||
|
path: 'PrismLauncher-source'
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
- name: Grab and store version
|
- name: Grab and store version
|
||||||
run: |
|
run: |
|
||||||
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
||||||
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
||||||
|
- name: Package artifacts properly
|
||||||
|
run: |
|
||||||
|
mv ${{ github.workspace }}/PrismLauncher-source PrismLauncher-${{ env.VERSION }}
|
||||||
|
mv PrismLauncher-Linux-Qt6-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
|
mv PrismLauncher-Linux-Qt6*/PrismLauncher.tar.gz PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||||
|
mv PrismLauncher-Linux-Portable*/PrismLauncher-portable.tar.gz PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||||
|
mv PrismLauncher-Linux*/PrismLauncher.tar.gz PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||||
|
mv PrismLauncher-*.AppImage/PrismLauncher-*.AppImage PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||||
|
mv PrismLauncher-macOS-Legacy*/PrismLauncher.tar.gz PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||||
|
mv PrismLauncher-macOS*/PrismLauncher.tar.gz PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||||
|
|
||||||
|
tar -czf PrismLauncher-${{ env.VERSION }}.tar.gz PrismLauncher-${{ env.VERSION }}
|
||||||
|
|
||||||
|
for d in PrismLauncher-Windows-MSVC*; do
|
||||||
|
cd "${d}" || continue
|
||||||
|
LEGACY="$(echo -n ${d} | grep -o Legacy || true)"
|
||||||
|
ARM64="$(echo -n ${d} | grep -o arm64 || true)"
|
||||||
|
INST="$(echo -n ${d} | grep -o Setup || true)"
|
||||||
|
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
||||||
|
NAME="PrismLauncher-Windows-MSVC"
|
||||||
|
test -z "${LEGACY}" || NAME="${NAME}-Legacy"
|
||||||
|
test -z "${ARM64}" || NAME="${NAME}-arm64"
|
||||||
|
test -z "${PORT}" || NAME="${NAME}-Portable"
|
||||||
|
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
||||||
|
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
||||||
|
cd ..
|
||||||
|
done
|
||||||
|
|
||||||
|
for d in PrismLauncher-Windows-MinGW-w64*; do
|
||||||
|
cd "${d}" || continue
|
||||||
|
INST="$(echo -n ${d} | grep -o Setup || true)"
|
||||||
|
PORT="$(echo -n ${d} | grep -o Portable || true)"
|
||||||
|
NAME="PrismLauncher-Windows-MinGW-w64"
|
||||||
|
test -z "${PORT}" || NAME="${NAME}-Portable"
|
||||||
|
test -z "${INST}" || mv PrismLauncher-*.exe ../${NAME}-Setup-${{ env.VERSION }}.exe
|
||||||
|
test -n "${INST}" || zip -r -9 "../${NAME}-${{ env.VERSION }}.zip" *
|
||||||
|
cd ..
|
||||||
|
done
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
@ -30,70 +82,27 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref }}
|
tag_name: ${{ github.ref }}
|
||||||
name: PolyMC ${{ env.VERSION }}
|
name: Prism Launcher ${{ env.VERSION }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
files: |
|
||||||
upload_release:
|
PrismLauncher-Linux-${{ env.VERSION }}.tar.gz
|
||||||
needs: create_release
|
PrismLauncher-Linux-Portable-${{ env.VERSION }}.tar.gz
|
||||||
runs-on: ubuntu-latest
|
PrismLauncher-Linux-${{ env.VERSION }}-x86_64.AppImage
|
||||||
steps:
|
PrismLauncher-Linux-Qt6-${{ env.VERSION }}.tar.gz
|
||||||
|
PrismLauncher-Linux-Qt6-Portable-${{ env.VERSION }}.tar.gz
|
||||||
- name: Download artifacts
|
PrismLauncher-Windows-MinGW-w64-${{ env.VERSION }}.zip
|
||||||
uses: actions/download-artifact@v2
|
PrismLauncher-Windows-MinGW-w64-Portable-${{ env.VERSION }}.zip
|
||||||
|
PrismLauncher-Windows-MinGW-w64-Setup-${{ env.VERSION }}.exe
|
||||||
- name: Grab and store version
|
PrismLauncher-Windows-MSVC-Legacy-${{ env.VERSION }}.zip
|
||||||
run: |
|
PrismLauncher-Windows-MSVC-Legacy-Portable-${{ env.VERSION }}.zip
|
||||||
tag_name=$(echo ${{ github.ref }} | grep -oE "[^/]+$")
|
PrismLauncher-Windows-MSVC-Legacy-Setup-${{ env.VERSION }}.exe
|
||||||
echo "VERSION=$tag_name" >> $GITHUB_ENV
|
PrismLauncher-Windows-MSVC-arm64-${{ env.VERSION }}.zip
|
||||||
|
PrismLauncher-Windows-MSVC-arm64-Portable-${{ env.VERSION }}.zip
|
||||||
- name: Package artifacts properly
|
PrismLauncher-Windows-MSVC-arm64-Setup-${{ env.VERSION }}.exe
|
||||||
run: |
|
PrismLauncher-Windows-MSVC-${{ env.VERSION }}.zip
|
||||||
mv PolyMC-Linux*/PolyMC.tar.gz PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
PrismLauncher-Windows-MSVC-Portable-${{ env.VERSION }}.zip
|
||||||
mv PolyMC-*.AppImage/PolyMC-*.AppImage PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
PrismLauncher-Windows-MSVC-Setup-${{ env.VERSION }}.exe
|
||||||
mv PolyMC-Windows* PolyMC-Windows-${{ env.VERSION }}
|
PrismLauncher-macOS-${{ env.VERSION }}.tar.gz
|
||||||
mv PolyMC-macOS*/PolyMC.tar.gz PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
PrismLauncher-macOS-Legacy-${{ env.VERSION }}.tar.gz
|
||||||
|
PrismLauncher-${{ env.VERSION }}.tar.gz
|
||||||
cd PolyMC-Windows-${{ env.VERSION }}
|
|
||||||
zip -r -9 ../PolyMC-Windows-${{ env.VERSION }}.zip *
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
- name: Upload Linux asset
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
|
||||||
asset_name: PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
|
||||||
asset_path: PolyMC-Linux-${{ env.VERSION }}.tar.gz
|
|
||||||
asset_content_type: application/gzip
|
|
||||||
|
|
||||||
- name: Upload Linux AppImage asset
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
|
||||||
asset_name: PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
|
||||||
asset_path: PolyMC-Linux-${{ env.VERSION }}-x86_64.AppImage
|
|
||||||
asset_content_type: application/x-executable
|
|
||||||
|
|
||||||
- name: Upload Windows asset
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
|
||||||
asset_name: PolyMC-Windows-${{ env.VERSION }}.zip
|
|
||||||
asset_path: PolyMC-Windows-${{ env.VERSION }}.zip
|
|
||||||
asset_content_type: application/zip
|
|
||||||
|
|
||||||
- name: Upload macOS asset
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ needs.create_release.outputs.upload_url }}
|
|
||||||
asset_name: PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
|
||||||
asset_path: PolyMC-macOS-${{ env.VERSION }}.tar.gz
|
|
||||||
asset_content_type: application/gzip
|
|
||||||
|
15
.github/workflows/winget.yml
vendored
Normal file
15
.github/workflows/winget.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
name: Publish to WinGet
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [released]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||||
|
with:
|
||||||
|
identifier: PrismLauncher.PrismLauncher
|
||||||
|
version: ${{ github.event.release.tag_name }}
|
||||||
|
installers-regex: 'PrismLauncher-Windows-MSVC(:?-arm64|-Legacy)?-Setup-.+\.exe$'
|
||||||
|
token: ${{ secrets.WINGET_TOKEN }}
|
16
.gitignore
vendored
16
.gitignore
vendored
@ -11,9 +11,14 @@ html/
|
|||||||
*.pro.user
|
*.pro.user
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
CMakeLists.txt.user.*
|
CMakeLists.txt.user.*
|
||||||
|
CMakeSettings.json
|
||||||
|
/CMakeFiles
|
||||||
|
CMakeCache.txt
|
||||||
/.project
|
/.project
|
||||||
/.settings
|
/.settings
|
||||||
/.idea
|
/.idea
|
||||||
|
/.vscode
|
||||||
|
/.vs
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
Debug
|
Debug
|
||||||
|
|
||||||
@ -41,4 +46,13 @@ run/
|
|||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
# Nix/NixOS
|
# Nix/NixOS
|
||||||
result/
|
.direnv/
|
||||||
|
.pre-commit-config.yaml
|
||||||
|
result
|
||||||
|
|
||||||
|
# Flatpak
|
||||||
|
.flatpak-builder
|
||||||
|
flatbuild
|
||||||
|
|
||||||
|
# Snap
|
||||||
|
*.snap
|
||||||
|
23
.gitmodules
vendored
23
.gitmodules
vendored
@ -1,8 +1,21 @@
|
|||||||
[submodule "depends/libnbtplusplus"]
|
|
||||||
path = libraries/libnbtplusplus
|
|
||||||
url = https://github.com/MultiMC/libnbtplusplus.git
|
|
||||||
pushurl = git@github.com:MultiMC/libnbtplusplus.git
|
|
||||||
|
|
||||||
[submodule "libraries/quazip"]
|
[submodule "libraries/quazip"]
|
||||||
path = libraries/quazip
|
path = libraries/quazip
|
||||||
url = https://github.com/stachenov/quazip.git
|
url = https://github.com/stachenov/quazip.git
|
||||||
|
[submodule "libraries/tomlplusplus"]
|
||||||
|
path = libraries/tomlplusplus
|
||||||
|
url = https://github.com/marzer/tomlplusplus.git
|
||||||
|
[submodule "libraries/filesystem"]
|
||||||
|
path = libraries/filesystem
|
||||||
|
url = https://github.com/gulrak/filesystem
|
||||||
|
[submodule "libraries/libnbtplusplus"]
|
||||||
|
path = libraries/libnbtplusplus
|
||||||
|
url = https://github.com/PrismLauncher/libnbtplusplus.git
|
||||||
|
[submodule "libraries/zlib"]
|
||||||
|
path = libraries/zlib
|
||||||
|
url = https://github.com/madler/zlib.git
|
||||||
|
[submodule "libraries/extra-cmake-modules"]
|
||||||
|
path = libraries/extra-cmake-modules
|
||||||
|
url = https://github.com/KDE/extra-cmake-modules
|
||||||
|
[submodule "libraries/cmark"]
|
||||||
|
path = libraries/cmark
|
||||||
|
url = https://github.com/commonmark/cmark.git
|
||||||
|
12
.markdownlint.yaml
Normal file
12
.markdownlint.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# MD013/line-length - Line length
|
||||||
|
MD013: false
|
||||||
|
|
||||||
|
# MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content
|
||||||
|
MD024:
|
||||||
|
siblings-only: true
|
||||||
|
|
||||||
|
# MD033/no-inline-html Inline HTML
|
||||||
|
MD033: false
|
||||||
|
|
||||||
|
# MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading
|
||||||
|
MD041: false
|
2
.markdownlintignore
Normal file
2
.markdownlintignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
libraries/nbtplusplus
|
||||||
|
libraries/quazip
|
4
BUILD.md
4
BUILD.md
@ -1,5 +1,3 @@
|
|||||||
# Build Instructions
|
# Build Instructions
|
||||||
|
|
||||||
Build instructions are available on [the website](https://polymc.org/wiki/development/build-instructions/).
|
Full build instructions are available on [the website](https://prismlauncher.org/wiki/development/build-instructions/).
|
||||||
|
|
||||||
If you would like to contribute or fix an issue with the Build instructions you can do so [here](https://github.com/PolyMC/polymc.github.io/blob/master/src/wiki/development/build-instructions.md).
|
|
||||||
|
382
CMakeLists.txt
382
CMakeLists.txt
@ -1,19 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.15) # minimum version required by QuaZip
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
|
|
||||||
cmake_policy(SET CMP0020 OLD)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
project(Launcher)
|
project(Launcher)
|
||||||
enable_testing()
|
|
||||||
|
|
||||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||||
if(IS_IN_SOURCE_BUILD)
|
if(IS_IN_SOURCE_BUILD)
|
||||||
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
|
message(FATAL_ERROR "You are building the Launcher in-source. Please separate the build tree from the source tree.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
##################################### Set CMake options #####################################
|
##################################### Set CMake options #####################################
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
@ -31,155 +24,302 @@ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
|
|||||||
######## Set compiler flags ########
|
######## Set compiler flags ########
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED true)
|
set(CMAKE_CXX_STANDARD_REQUIRED true)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED true)
|
set(CMAKE_C_STANDARD_REQUIRED true)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
include(GenerateExportHeader)
|
include(GenerateExportHeader)
|
||||||
set(CMAKE_CXX_FLAGS " -Wall -pedantic -Werror -Wno-deprecated-declarations -D_GLIBCXX_USE_CXX11_ABI=0 -fstack-protector-strong --param=ssp-buffer-size=4 -O3 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS}")
|
if(MSVC)
|
||||||
if(UNIX AND APPLE)
|
# /GS Adds buffer security checks, default on but incuded anyway to mirror gcc's fstack-protector flag
|
||||||
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
# /permissive- specify standards-conforming compiler behavior, also enabled by Qt6, default on with std:c++20
|
||||||
|
# Use /W4 as /Wall includes unnesserey warnings such as added padding to structs
|
||||||
|
set(CMAKE_CXX_FLAGS "/GS /permissive- /W4 ${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
|
# LINK accepts /SUBSYSTEM whics sets if we are a WINDOWS (gui) or a CONSOLE programs
|
||||||
|
# This implicitly selects an entrypoint specific to the subsystem selected
|
||||||
|
# qtmain/QtEntryPointLib provides the correct entrypoint (wWinMain) for gui programs
|
||||||
|
# Additinaly LINK autodetects we use a GUI so we can omit /SUBSYSTEM
|
||||||
|
# This allows tests to still use have console without using seperate linker flags
|
||||||
|
# /LTCG allows for linking wholy optimizated programs
|
||||||
|
# /MANIFEST:NO disables generating a manifest file, we instead provide our own
|
||||||
|
# /STACK sets the stack reserve size, ATL's pack list needs 3-4 MiB as of November 2022, provide 8 MiB
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "/LTCG /MANIFEST:NO /STACK:8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
|
||||||
|
# /GL enables whole program optimizations
|
||||||
|
# /Gw helps reduce binary size
|
||||||
|
# /Gy allows the compiler to package individual functions
|
||||||
|
# /guard:cf enables control flow guard
|
||||||
|
foreach(lang C CXX)
|
||||||
|
set("CMAKE_${lang}_FLAGS_RELEASE" "/GL /Gw /Gy /guard:cf")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# See https://github.com/ccache/ccache/issues/1040
|
||||||
|
# Note, CMake 3.25 replaces this with CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
|
||||||
|
# See https://cmake.org/cmake/help/v3.25/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.html
|
||||||
|
foreach(config DEBUG RELWITHDEBINFO)
|
||||||
|
foreach(lang C CXX)
|
||||||
|
set(flags_var "CMAKE_${lang}_FLAGS_${config}")
|
||||||
|
string(REGEX REPLACE "/Z[Ii]" "/Z7" ${flags_var} "${${flags_var}}")
|
||||||
|
endforeach()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(CMAKE_MSVC_RUNTIME_LIBRARY STREQUAL "MultiThreadedDLL")
|
||||||
|
set(CMAKE_MAP_IMPORTED_CONFIG_DEBUG Release "")
|
||||||
|
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO Release "")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(CMAKE_CXX_FLAGS "-Wall -pedantic -fstack-protector-strong --param=ssp-buffer-size=4 ${CMAKE_CXX_FLAGS}")
|
||||||
|
|
||||||
|
# ATL's pack list needs more than the default 1 Mib stack on windows
|
||||||
|
if(WIN32)
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--stack,8388608 ${CMAKE_EXE_LINKER_FLAGS}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
|
|
||||||
|
|
||||||
# Fix build with Qt 5.13
|
# Fix build with Qt 5.13
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_NO_DEPRECATED_WARNINGS=Y")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DQT_DISABLE_DEPRECATED_BEFORE=0x050C00")
|
||||||
|
|
||||||
|
# Fix aarch64 build for toml++
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTOML_ENABLE_FLOAT16=0")
|
||||||
|
|
||||||
|
# set CXXFLAGS for build targets
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -D_FORTIFY_SOURCE=2 ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||||
|
|
||||||
|
option(ENABLE_LTO "Enable Link Time Optimization" off)
|
||||||
|
|
||||||
|
if(ENABLE_LTO)
|
||||||
|
include(CheckIPOSupported)
|
||||||
|
check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error)
|
||||||
|
|
||||||
|
if(ipo_supported)
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
|
||||||
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL TRUE)
|
||||||
|
if(CMAKE_BUILD_TYPE)
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
|
||||||
|
message(STATUS "IPO / LTO enabled")
|
||||||
|
else()
|
||||||
|
message(STATUS "Not enabling IPO / LTO on debug builds")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "IPO / LTO will only be enabled for release builds")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "IPO / LTO not supported: <${ipo_error}>")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(BUILD_TESTING "Build the testing tree." ON)
|
||||||
|
|
||||||
|
find_package(ECM QUIET NO_MODULE)
|
||||||
|
if(NOT ECM_FOUND)
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/CMakeLists.txt")
|
||||||
|
message(STATUS "Using bundled ECM")
|
||||||
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/libraries/extra-cmake-modules/modules;${CMAKE_MODULE_PATH}")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR
|
||||||
|
" Could not find ECM\n \n"
|
||||||
|
" Either install ECM using the system package manager or clone submodules\n"
|
||||||
|
" Submodules can be cloned with 'git submodule update --init --recursive'")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(CMAKE_MODULE_PATH "${ECM_MODULE_PATH};${CMAKE_MODULE_PATH}")
|
||||||
|
endif()
|
||||||
|
include(CTest)
|
||||||
|
include(ECMAddTests)
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
enable_testing()
|
||||||
|
endif()
|
||||||
|
|
||||||
##################################### Set Application options #####################################
|
##################################### Set Application options #####################################
|
||||||
|
|
||||||
######## Set URLs ########
|
######## Set URLs ########
|
||||||
set(Launcher_NEWS_RSS_URL "https://polymc.org/feed/feed.xml" CACHE STRING "URL to fetch PolyMC's news RSS feed from.")
|
set(Launcher_NEWS_RSS_URL "https://prismlauncher.org/feed/feed.xml" CACHE STRING "URL to fetch Prism Launcher's news RSS feed from.")
|
||||||
set(Launcher_NEWS_OPEN_URL "https://polymc.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
set(Launcher_NEWS_OPEN_URL "https://prismlauncher.org/news" CACHE STRING "URL that gets opened when the user clicks 'More News'")
|
||||||
set(Launcher_HELP_URL "https://polymc.org/wiki/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
set(Launcher_HELP_URL "https://prismlauncher.org/wiki/help-pages/%1" CACHE STRING "URL (with arg %1 to be substituted with page-id) that gets opened when the user requests help")
|
||||||
|
|
||||||
######## Set version numbers ########
|
######## Set version numbers ########
|
||||||
set(Launcher_VERSION_MAJOR 1)
|
set(Launcher_VERSION_MAJOR 7)
|
||||||
set(Launcher_VERSION_MINOR 1)
|
set(Launcher_VERSION_MINOR 0)
|
||||||
set(Launcher_VERSION_HOTFIX 0)
|
|
||||||
|
|
||||||
# Build number
|
set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}")
|
||||||
set(Launcher_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.0.0")
|
||||||
|
set(Launcher_VERSION_NAME4_COMMA "${Launcher_VERSION_MAJOR},${Launcher_VERSION_MINOR},0,0")
|
||||||
|
|
||||||
# Build platform.
|
# Build platform.
|
||||||
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
|
set(Launcher_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used to display in the about dialog.")
|
||||||
|
|
||||||
# Channel list URL
|
# Channel list URL
|
||||||
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
set(Launcher_UPDATER_BASE "" CACHE STRING "Base URL for the updater.")
|
||||||
|
|
||||||
# Notification URL
|
|
||||||
set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
|
|
||||||
|
|
||||||
# The metadata server
|
# The metadata server
|
||||||
set(Launcher_META_URL "https://meta.polymc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
set(Launcher_META_URL "https://meta.prismlauncher.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
|
||||||
|
|
||||||
# Imgur API Client ID
|
# Imgur API Client ID
|
||||||
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
||||||
|
|
||||||
# MSA Client ID
|
|
||||||
set(Launcher_MSA_CLIENT_ID "549033b2-1532-4d4e-ae77-1bbaa46f9d74" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
|
||||||
|
|
||||||
# Bug tracker URL
|
# Bug tracker URL
|
||||||
set(Launcher_BUG_TRACKER_URL "https://github.com/PolyMC/PolyMC/issues" CACHE STRING "URL for the bug tracker.")
|
set(Launcher_BUG_TRACKER_URL "https://github.com/PrismLauncher/PrismLauncher/issues" CACHE STRING "URL for the bug tracker.")
|
||||||
|
|
||||||
# Translations Platform URL
|
# Translations Platform URL
|
||||||
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/polymc/polymc/" CACHE STRING "URL for the translations platform.")
|
set(Launcher_TRANSLATIONS_URL "https://hosted.weblate.org/projects/prismlauncher/launcher/" CACHE STRING "URL for the translations platform.")
|
||||||
|
|
||||||
# Matrix Space
|
# Matrix Space
|
||||||
set(Launcher_MATRIX_URL "https://matrix.to/#/#polymc:polymc.org" CACHE STRING "URL to the Matrix Space")
|
set(Launcher_MATRIX_URL "https://prismlauncher.org/matrix" CACHE STRING "URL to the Matrix Space")
|
||||||
|
|
||||||
# Discord URL
|
# Discord URL
|
||||||
set(Launcher_DISCORD_URL "https://discord.gg/Z52pwxWCHP" CACHE STRING "URL for the Discord guild.")
|
set(Launcher_DISCORD_URL "https://prismlauncher.org/discord" CACHE STRING "URL for the Discord guild.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Subreddit URL
|
# Subreddit URL
|
||||||
set(Launcher_SUBREDDIT_URL "" CACHE STRING "URL for the subreddit.")
|
set(Launcher_SUBREDDIT_URL "https://prismlauncher.org/reddit" CACHE STRING "URL for the subreddit.")
|
||||||
|
|
||||||
# Builds
|
# Builds
|
||||||
# TODO: Launcher_FORCE_BUNDLED_LIBS should be off in the future, but as of QuaZip 1.2, we can't do that yet.
|
set(Launcher_FORCE_BUNDLED_LIBS OFF CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
||||||
set(Launcher_FORCE_BUNDLED_LIBS ON CACHE BOOL "Prevent using system libraries, if they are available as submodules")
|
set(Launcher_QT_VERSION_MAJOR "6" CACHE STRING "Major Qt version to build against")
|
||||||
set(Launcher_QT_VERSION_MAJOR "5" CACHE STRING "Major Qt version to build against")
|
|
||||||
|
# API Keys
|
||||||
|
# NOTE: These API keys are here for convenience. If you rebrand this software or intend to break the terms of service
|
||||||
|
# of these platforms, please change these API keys beforehand.
|
||||||
|
# Be aware that if you were to use these API keys for malicious purposes they might get revoked, which might cause
|
||||||
|
# breakage to thousands of users.
|
||||||
|
# If you don't plan to use these features of this software, you can just remove these values.
|
||||||
|
|
||||||
|
# By using this key in your builds you accept the terms of use laid down in
|
||||||
|
# https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use
|
||||||
|
set(Launcher_MSA_CLIENT_ID "c36a9fb6-4f2a-41ff-90bd-ae7cc92031eb" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
||||||
|
|
||||||
|
# By using this key in your builds you accept the terms and conditions laid down in
|
||||||
|
# https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions
|
||||||
|
# NOTE: CurseForge requires you to change this if you make any kind of derivative work.
|
||||||
|
# This key was issued specifically for Prism Launcher
|
||||||
|
set(Launcher_CURSEFORGE_API_KEY "$2a$10$wuAJuNZuted3NORVmpgUC.m8sI.pv1tOPKZyBgLFGjxFp/br0lZCC" CACHE STRING "API key for the CurseForge platform")
|
||||||
|
|
||||||
|
|
||||||
#### Check the current Git commit and branch
|
#### Check the current Git commit and branch
|
||||||
include(GetGitRevisionDescription)
|
include(GetGitRevisionDescription)
|
||||||
|
git_get_exact_tag(Launcher_GIT_TAG)
|
||||||
get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
|
get_git_head_revision(Launcher_GIT_REFSPEC Launcher_GIT_COMMIT)
|
||||||
|
|
||||||
message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
|
message(STATUS "Git commit: ${Launcher_GIT_COMMIT}")
|
||||||
|
message(STATUS "Git tag: ${Launcher_GIT_TAG}")
|
||||||
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
message(STATUS "Git refspec: ${Launcher_GIT_REFSPEC}")
|
||||||
|
|
||||||
set(Launcher_RELEASE_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}")
|
|
||||||
string(TIMESTAMP TODAY "%Y-%m-%d")
|
string(TIMESTAMP TODAY "%Y-%m-%d")
|
||||||
set(Launcher_RELEASE_TIMESTAMP "${TODAY}")
|
set(Launcher_BUILD_TIMESTAMP "${TODAY}")
|
||||||
|
|
||||||
#### Custom target to just print the version.
|
|
||||||
add_custom_target(version echo "Version: ${Launcher_RELEASE_VERSION_NAME}")
|
|
||||||
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.LAUNCHER_VERSION\\' value=\\'${Launcher_RELEASE_VERSION_NAME}\\']")
|
|
||||||
|
|
||||||
################################ 3rd Party Libs ################################
|
################################ 3rd Party Libs ################################
|
||||||
|
|
||||||
|
# Successive configurations of cmake without cleaning the build dir will cause zlib fallback to fail due to cached values
|
||||||
|
# Record when fallback triggered and skip this find_package
|
||||||
|
if(NOT Launcher_FORCE_BUNDLED_LIBS AND NOT FORCE_BUNDLED_ZLIB)
|
||||||
|
find_package(ZLIB QUIET)
|
||||||
|
endif()
|
||||||
|
if(NOT ZLIB_FOUND)
|
||||||
|
set(FORCE_BUNDLED_ZLIB TRUE CACHE BOOL "")
|
||||||
|
mark_as_advanced(FORCE_BUNDLED_ZLIB)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Find the required Qt parts
|
# Find the required Qt parts
|
||||||
|
include(QtVersionlessBackport)
|
||||||
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||||
set(QT_VERSION_MAJOR 5)
|
set(QT_VERSION_MAJOR 5)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
|
find_package(Qt5 REQUIRED COMPONENTS Core Widgets Concurrent Network Test Xml)
|
||||||
|
|
||||||
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||||
find_package(QuaZip-Qt5 REQUIRED)
|
find_package(QuaZip-Qt5 1.3 QUIET)
|
||||||
endif()
|
endif()
|
||||||
if (NOT QuaZip-Qt5_FOUND)
|
if (NOT QuaZip-Qt5_FOUND)
|
||||||
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
|
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
|
||||||
set(FORCE_BUNDLED_QUAZIP 1)
|
set(FORCE_BUNDLED_QUAZIP 1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Qt 6 sets these by default. Notably causes Windows APIs to use UNICODE strings.
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUNICODE -D_UNICODE")
|
||||||
|
elseif(Launcher_QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
set(QT_VERSION_MAJOR 6)
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Core CoreTools Widgets Concurrent Network Test Xml Core5Compat)
|
||||||
|
list(APPEND Launcher_QT_LIBS Qt6::Core5Compat)
|
||||||
|
|
||||||
|
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||||
|
find_package(QuaZip-Qt6 1.3 QUIET)
|
||||||
|
endif()
|
||||||
|
if (NOT QuaZip-Qt6_FOUND)
|
||||||
|
set(QUAZIP_QT_MAJOR_VERSION ${QT_VERSION_MAJOR} CACHE STRING "Qt version to use (4, 5 or 6), defaults to ${QT_VERSION_MAJOR}" FORCE)
|
||||||
|
set(FORCE_BUNDLED_QUAZIP 1)
|
||||||
|
endif()
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
|
message(FATAL_ERROR "Qt version ${Launcher_QT_VERSION_MAJOR} is not supported")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# The Qt5 cmake files don't provide its install paths, so ask qmake.
|
if(Launcher_QT_VERSION_MAJOR EQUAL 5)
|
||||||
include(QMakeQuery)
|
include(ECMQueryQt)
|
||||||
query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
|
ecm_query_qt(QT_PLUGINS_DIR QT_INSTALL_PLUGINS)
|
||||||
query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
|
ecm_query_qt(QT_LIBS_DIR QT_INSTALL_LIBS)
|
||||||
query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR)
|
ecm_query_qt(QT_LIBEXECS_DIR QT_INSTALL_LIBEXECS)
|
||||||
query_qmake(QT_INSTALL_LIBEXECS QT_LIBEXECS_DIR)
|
else()
|
||||||
query_qmake(QT_HOST_DATA QT_DATA_DIR)
|
set(QT_PLUGINS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_PLUGINS})
|
||||||
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
|
set(QT_LIBS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBS})
|
||||||
|
set(QT_LIBEXECS_DIR ${QT${QT_VERSION_MAJOR}_INSTALL_PREFIX}/${QT${QT_VERSION_MAJOR}_INSTALL_LIBEXECS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# NOTE: Qt 6 already sets this by default
|
||||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT Launcher_FORCE_BUNDLED_LIBS)
|
||||||
|
# Find toml++
|
||||||
|
find_package(tomlplusplus 3.2.0 QUIET)
|
||||||
|
|
||||||
|
# Find ghc_filesystem
|
||||||
|
find_package(ghc_filesystem QUIET)
|
||||||
|
|
||||||
|
# Find cmark
|
||||||
|
find_package(cmark QUIET)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(ECMQtDeclareLoggingCategory)
|
||||||
|
|
||||||
####################################### Program Info #######################################
|
####################################### Program Info #######################################
|
||||||
|
|
||||||
set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
|
set(Launcher_APP_BINARY_NAME "prismlauncher" CACHE STRING "Name of the Launcher binary")
|
||||||
add_subdirectory(program_info)
|
add_subdirectory(program_info)
|
||||||
|
|
||||||
####################################### Install layout #######################################
|
####################################### Install layout #######################################
|
||||||
|
|
||||||
# Install the build results according to platform
|
if(NOT (UNIX AND APPLE))
|
||||||
set(Launcher_PORTABLE 1 CACHE BOOL "The type of installation (Portable or System)")
|
# Install "portable.txt" if selected component is "portable"
|
||||||
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_Portable_File}" DESTINATION "." COMPONENT portable EXCLUDE_FROM_ALL)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(UNIX AND APPLE)
|
if(UNIX AND APPLE)
|
||||||
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
set(BINARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||||
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
set(LIBRARY_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||||
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
set(PLUGIN_DEST_DIR "${Launcher_Name}.app/Contents/MacOS")
|
||||||
|
set(FRAMEWORK_DEST_DIR "${Launcher_Name}.app/Contents/Frameworks")
|
||||||
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
|
set(RESOURCES_DEST_DIR "${Launcher_Name}.app/Contents/Resources")
|
||||||
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
|
set(JARS_DEST_DIR "${Launcher_Name}.app/Contents/MacOS/jars")
|
||||||
|
|
||||||
set(BUNDLE_DEST_DIR ".")
|
|
||||||
|
|
||||||
# Apps to bundle
|
# Apps to bundle
|
||||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
set(APPS "\${CMAKE_INSTALL_PREFIX}/${Launcher_Name}.app")
|
||||||
|
|
||||||
# Mac bundle settings
|
# Mac bundle settings
|
||||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_Name}")
|
set(MACOSX_BUNDLE_BUNDLE_NAME "${Launcher_DisplayName}")
|
||||||
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_Name}: Minecraft launcher and management utility.")
|
set(MACOSX_BUNDLE_INFO_STRING "${Launcher_DisplayName}: A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.")
|
||||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.polymc.${Launcher_Name}")
|
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.prismlauncher.${Launcher_Name}")
|
||||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
|
set(MACOSX_BUNDLE_BUNDLE_VERSION "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
|
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_HOTFIX}.${Launcher_VERSION_BUILD}")
|
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${Launcher_VERSION_NAME}")
|
||||||
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
set(MACOSX_BUNDLE_ICON_FILE ${Launcher_Name}.icns)
|
||||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015-2021 ${Launcher_Copyright}")
|
set(MACOSX_BUNDLE_COPYRIGHT "© 2022 ${Launcher_Copyright_Mac}")
|
||||||
|
set(MACOSX_SPARKLE_UPDATE_PUBLIC_KEY "v55ZWWD6QlPoXGV6VLzOTZxZUggWeE51X8cRQyQh6vA=")
|
||||||
|
set(MACOSX_SPARKLE_UPDATE_FEED_URL "https://prismlauncher.org/feed/appcast.xml")
|
||||||
|
|
||||||
|
set(MACOSX_SPARKLE_DOWNLOAD_URL "https://github.com/sparkle-project/Sparkle/releases/download/2.1.0/Sparkle-2.1.0.tar.xz" CACHE STRING "URL to Sparkle release archive")
|
||||||
|
set(MACOSX_SPARKLE_SHA256 "bf6ac1caa9f8d321d5784859c88da874f28412f37fb327bc21b7b14c5d61ef94" CACHE STRING "SHA256 checksum for Sparkle release archive")
|
||||||
|
set(MACOSX_SPARKLE_DIR "${CMAKE_BINARY_DIR}/frameworks/Sparkle")
|
||||||
|
|
||||||
# directories to look for dependencies
|
# directories to look for dependencies
|
||||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} ${MACOSX_SPARKLE_DIR})
|
||||||
|
|
||||||
# install as bundle
|
# install as bundle
|
||||||
set(INSTALL_BUNDLE "full")
|
set(INSTALL_BUNDLE "full")
|
||||||
@ -188,31 +328,11 @@ if(UNIX AND APPLE)
|
|||||||
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
install(FILES ${Launcher_Branding_ICNS} DESTINATION ${RESOURCES_DEST_DIR} RENAME ${Launcher_Name}.icns)
|
||||||
|
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
|
include(KDEInstallDirs)
|
||||||
|
|
||||||
set(BINARY_DEST_DIR "bin")
|
set(BINARY_DEST_DIR "bin")
|
||||||
if(Launcher_PORTABLE)
|
|
||||||
set(LIBRARY_DEST_DIR "bin")
|
|
||||||
set(BUNDLE_DEST_DIR ".")
|
|
||||||
set(JARS_DEST_DIR "bin/jars")
|
|
||||||
|
|
||||||
# launcher/Application.cpp will use this value
|
|
||||||
set(Launcher_APP_BINARY_DEFS "-DLAUNCHER_PORTABLE")
|
|
||||||
|
|
||||||
# Install basic runner script
|
|
||||||
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
|
|
||||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION ${BUNDLE_DEST_DIR} RENAME ${Launcher_Name})
|
|
||||||
else()
|
|
||||||
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
set(LIBRARY_DEST_DIR "lib${LIB_SUFFIX}")
|
||||||
set(JARS_DEST_DIR "share/jars")
|
set(JARS_DEST_DIR "share/${Launcher_APP_BINARY_NAME}")
|
||||||
set(LAUNCHER_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
|
|
||||||
set(LAUNCHER_METAINFO_DEST_DIR "share/metainfo" CACHE STRING "Path to the metainfo directory")
|
|
||||||
set(LAUNCHER_ICON_DEST_DIR "share/icons/hicolor/scalable/apps" CACHE STRING "Path to the scalable icon directory")
|
|
||||||
|
|
||||||
set(Launcher_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}")
|
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${LAUNCHER_DESKTOP_DEST_DIR})
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${LAUNCHER_METAINFO_DEST_DIR})
|
|
||||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION ${LAUNCHER_ICON_DEST_DIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# install as bundle with no dependencies included
|
# install as bundle with no dependencies included
|
||||||
set(INSTALL_BUNDLE "nodeps")
|
set(INSTALL_BUNDLE "nodeps")
|
||||||
@ -220,11 +340,25 @@ elseif(UNIX)
|
|||||||
# Set RPATH
|
# Set RPATH
|
||||||
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
SET(Launcher_BINARY_RPATH "$ORIGIN/")
|
||||||
|
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_Desktop} DESTINATION ${KDE_INSTALL_APPDIR})
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_MetaInfo} DESTINATION ${KDE_INSTALL_METAINFODIR})
|
||||||
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_SVG} DESTINATION "${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps")
|
||||||
|
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${Launcher_mrpack_MIMEInfo} DESTINATION ${KDE_INSTALL_MIMEDIR})
|
||||||
|
|
||||||
|
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/launcher/qtlogging.ini" DESTINATION "${KDE_INSTALL_DATADIR}/${Launcher_Name}")
|
||||||
|
|
||||||
|
if(Launcher_ManPage)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${Launcher_ManPage} DESTINATION "${KDE_INSTALL_MANDIR}/man6")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install basic runner script if component "portable" is selected
|
||||||
|
configure_file(launcher/Launcher.in "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" @ONLY)
|
||||||
|
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION "." RENAME ${Launcher_Name} COMPONENT portable EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
elseif(WIN32)
|
elseif(WIN32)
|
||||||
set(BINARY_DEST_DIR ".")
|
set(BINARY_DEST_DIR ".")
|
||||||
set(LIBRARY_DEST_DIR ".")
|
set(LIBRARY_DEST_DIR ".")
|
||||||
set(PLUGIN_DEST_DIR ".")
|
set(PLUGIN_DEST_DIR ".")
|
||||||
set(BUNDLE_DEST_DIR ".")
|
|
||||||
set(RESOURCES_DEST_DIR ".")
|
set(RESOURCES_DEST_DIR ".")
|
||||||
set(JARS_DEST_DIR "jars")
|
set(JARS_DEST_DIR "jars")
|
||||||
|
|
||||||
@ -240,6 +374,8 @@ else()
|
|||||||
message(FATAL_ERROR "Platform not supported")
|
message(FATAL_ERROR "Platform not supported")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
################################ Included Libs ################################
|
################################ Included Libs ################################
|
||||||
|
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
@ -251,27 +387,77 @@ option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
|
|||||||
add_subdirectory(libraries/libnbtplusplus)
|
add_subdirectory(libraries/libnbtplusplus)
|
||||||
|
|
||||||
add_subdirectory(libraries/systeminfo) # system information library
|
add_subdirectory(libraries/systeminfo) # system information library
|
||||||
add_subdirectory(libraries/hoedown) # markdown parser
|
|
||||||
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
||||||
add_subdirectory(libraries/javacheck) # java compatibility checker
|
add_subdirectory(libraries/javacheck) # java compatibility checker
|
||||||
add_subdirectory(libraries/xz-embedded) # xz compression
|
if(FORCE_BUNDLED_ZLIB)
|
||||||
|
message(STATUS "Using bundled zlib")
|
||||||
|
|
||||||
|
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # Suppress cmake warnings and allow INTERPROCEDURAL_OPTIMIZATION for zlib
|
||||||
|
set(SKIP_INSTALL_ALL ON)
|
||||||
|
add_subdirectory(libraries/zlib EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
|
# On OS where unistd.h exists, zlib's generated header defines `Z_HAVE_UNISTD_H`, while the included header does not.
|
||||||
|
# We cannot safely undo the rename on those systems, and they generally have packages for zlib anyway.
|
||||||
|
check_include_file(unistd.h NEED_GENERATED_ZCONF)
|
||||||
|
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" AND NOT NEED_GENERATED_ZCONF)
|
||||||
|
# zlib's cmake script renames a file, dirtying the submodule, see https://github.com/madler/zlib/issues/162
|
||||||
|
message(STATUS "Undoing Rename")
|
||||||
|
message(STATUS " ${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
|
||||||
|
file(RENAME "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h.included" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib/zconf.h")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/libraries/zlib" "${CMAKE_CURRENT_SOURCE_DIR}/libraries/zlib" CACHE STRING "" FORCE)
|
||||||
|
set_target_properties(zlibstatic PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIR}")
|
||||||
|
add_library(ZLIB::ZLIB ALIAS zlibstatic)
|
||||||
|
set(ZLIB_LIBRARY ZLIB::ZLIB CACHE STRING "zlib library name")
|
||||||
|
|
||||||
|
find_package(ZLIB REQUIRED)
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system zlib")
|
||||||
|
endif()
|
||||||
if (FORCE_BUNDLED_QUAZIP)
|
if (FORCE_BUNDLED_QUAZIP)
|
||||||
message(STATUS "Using bundled QuaZip")
|
message(STATUS "Using bundled QuaZip")
|
||||||
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
set(BUILD_SHARED_LIBS 0) # link statically to avoid conflicts.
|
||||||
set(QUAZIP_INSTALL 0)
|
set(QUAZIP_INSTALL 0)
|
||||||
add_subdirectory(libraries/quazip) # zip manipulation library
|
add_subdirectory(libraries/quazip) # zip manipulation library
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system QuaZip")
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||||
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
|
||||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||||
add_subdirectory(libraries/classparser) # class parser library
|
if(NOT tomlplusplus_FOUND)
|
||||||
add_subdirectory(libraries/optional-bare)
|
message(STATUS "Using bundled tomlplusplus")
|
||||||
add_subdirectory(libraries/tomlc99) # toml parser
|
add_subdirectory(libraries/tomlplusplus) # toml parser
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system tomlplusplus")
|
||||||
|
endif()
|
||||||
|
if(NOT cmark_FOUND)
|
||||||
|
message(STATUS "Using bundled cmark")
|
||||||
|
set(CMARK_STATIC ON CACHE BOOL "Build static libcmark library" FORCE)
|
||||||
|
set(CMARK_SHARED OFF CACHE BOOL "Build shared libcmark library" FORCE)
|
||||||
|
set(CMARK_TESTS OFF CACHE BOOL "Build cmark tests and enable testing" FORCE)
|
||||||
|
add_subdirectory(libraries/cmark EXCLUDE_FROM_ALL) # Markdown parser
|
||||||
|
add_library(cmark::cmark ALIAS cmark_static)
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system cmark")
|
||||||
|
endif()
|
||||||
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
||||||
|
add_subdirectory(libraries/gamemode)
|
||||||
|
add_subdirectory(libraries/murmur2) # Hash for usage with the CurseForge API
|
||||||
|
if (NOT ghc_filesystem_FOUND)
|
||||||
|
message(STATUS "Using bundled ghc_filesystem")
|
||||||
|
add_subdirectory(libraries/filesystem) # Implementation of std::filesystem for old C++, for usage in old macOS
|
||||||
|
else()
|
||||||
|
message(STATUS "Using system ghc_filesystem")
|
||||||
|
endif()
|
||||||
|
add_subdirectory(libraries/qdcss) # css parser
|
||||||
|
|
||||||
############################### Built Artifacts ###############################
|
############################### Built Artifacts ###############################
|
||||||
|
|
||||||
add_subdirectory(buildconfig)
|
add_subdirectory(buildconfig)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
# NOTE: this must always be last to appease the CMake deity of quirky install command evaluation order.
|
||||||
add_subdirectory(launcher)
|
add_subdirectory(launcher)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Contributor Covenant Code of Conduct
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
This is a modified version of the Contributor Covenant.
|
This is a modified version of the Contributor Covenant.
|
||||||
See commit history to see our changes.
|
See commit history to see our changes.
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ representative at an online or offline event.
|
|||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
reported to the community leaders responsible for enforcement via email at
|
reported to the community leaders responsible for enforcement via email at
|
||||||
[polymc-enforcement@scrumplex.net](mailto:polymc-enforcement@scrumplex.net) (Email
|
[coc@scrumplex.net](mailto:coc@scrumplex.net) (Email
|
||||||
address subject to change).
|
address subject to change).
|
||||||
All complaints will be reviewed and investigated promptly and fairly.
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
@ -133,4 +134,3 @@ For answers to common questions about this code of conduct, see the FAQ at
|
|||||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||||
[FAQ]: https://www.contributor-covenant.org/faq
|
[FAQ]: https://www.contributor-covenant.org/faq
|
||||||
[translations]: https://www.contributor-covenant.org/translations
|
[translations]: https://www.contributor-covenant.org/translations
|
||||||
|
|
||||||
|
63
CONTRIBUTING.md
Normal file
63
CONTRIBUTING.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Contributions Guidelines
|
||||||
|
|
||||||
|
## Code formatting
|
||||||
|
|
||||||
|
Try to follow the existing formatting.
|
||||||
|
If there is no existing formatting, you may use `clang-format` with our included `.clang-format` configuration.
|
||||||
|
|
||||||
|
In general, in order of importance:
|
||||||
|
|
||||||
|
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
||||||
|
- Prefer readability over dogma.
|
||||||
|
- Keep to the existing formatting.
|
||||||
|
- Indent with 4 space unless it's in a submodule.
|
||||||
|
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
|
||||||
|
|
||||||
|
## Signing your work
|
||||||
|
|
||||||
|
In an effort to ensure that the code you contribute is actually compatible with the licenses in this codebase, we require you to sign-off all your contributions.
|
||||||
|
|
||||||
|
This can be done by appending `-s` to your `git commit` call, or by manually appending the following text to your commit message:
|
||||||
|
|
||||||
|
```
|
||||||
|
<commit message>
|
||||||
|
|
||||||
|
Signed-off-by: Author name <Author email>
|
||||||
|
```
|
||||||
|
|
||||||
|
By signing off your work, you agree to the terms below:
|
||||||
|
|
||||||
|
```
|
||||||
|
Developer's Certificate of Origin 1.1
|
||||||
|
|
||||||
|
By making a contribution to this project, I certify that:
|
||||||
|
|
||||||
|
(a) The contribution was created in whole or in part by me and I
|
||||||
|
have the right to submit it under the open source license
|
||||||
|
indicated in the file; or
|
||||||
|
|
||||||
|
(b) The contribution is based upon previous work that, to the best
|
||||||
|
of my knowledge, is covered under an appropriate open source
|
||||||
|
license and I have the right under that license to submit that
|
||||||
|
work with modifications, whether created in whole or in part
|
||||||
|
by me, under the same open source license (unless I am
|
||||||
|
permitted to submit under a different license), as indicated
|
||||||
|
in the file; or
|
||||||
|
|
||||||
|
(c) The contribution was provided directly to me by some other
|
||||||
|
person who certified (a), (b) or (c) and I have not modified
|
||||||
|
it.
|
||||||
|
|
||||||
|
(d) I understand and agree that this project and the contribution
|
||||||
|
are public and that a record of the contribution (including all
|
||||||
|
personal information I submit with it, including my sign-off) is
|
||||||
|
maintained indefinitely and may be redistributed consistent with
|
||||||
|
this project or the open source license(s) involved.
|
||||||
|
```
|
||||||
|
|
||||||
|
These terms will be enforced once you create a pull request, and you will be informed automatically if any of your commits aren't signed-off by you.
|
||||||
|
|
||||||
|
As a bonus, you can also [cryptographically sign your commits][gh-signing-commits] and enable [vigilant mode][gh-vigilant-mode] on GitHub.
|
||||||
|
|
||||||
|
[gh-signing-commits]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits
|
||||||
|
[gh-vigilant-mode]: https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits
|
420
COPYING.md
420
COPYING.md
@ -1,12 +1,11 @@
|
|||||||
# PolyMC
|
## Prism Launcher
|
||||||
|
|
||||||
Copyright (C) 2012-2021 MultiMC Contributors
|
Prism Launcher - Minecraft Launcher
|
||||||
Copyright (C) 2021-2022 PolyMC Contributors
|
Copyright (C) 2022-2023 Prism Launcher Contributors
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
the Free Software Foundation, version 3.
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
@ -16,8 +15,11 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# Launcher (https://github.com/MultiMC/Launcher)
|
This file incorporates work covered by the following copyright and
|
||||||
Copyright 2012-2021 MultiMC Contributors
|
permission notice:
|
||||||
|
|
||||||
|
Copyright 2013-2021 MultiMC Contributors
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
@ -30,36 +32,90 @@
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
|
|
||||||
# MinGW runtime (Windows)
|
## PolyMC
|
||||||
|
|
||||||
Copyright (c) 2012 MinGW.org project
|
PolyMC - Minecraft Launcher
|
||||||
|
Copyright (C) 2021-2022 PolyMC Contributors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
This program is free software: you can redistribute it and/or modify
|
||||||
copy of this software and associated documentation files (the "Software"),
|
it under the terms of the GNU General Public License as published by
|
||||||
to deal in the Software without restriction, including without limitation
|
the Free Software Foundation, version 3.
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice, this permission notice and the below disclaimer
|
This program is distributed in the hope that it will be useful,
|
||||||
shall be included in all copies or substantial portions of the Software.
|
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.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
You should have received a copy of the GNU General Public License
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
# Qt 5
|
This file incorporates work covered by the following copyright and
|
||||||
|
permission notice:
|
||||||
|
|
||||||
Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
Copyright 2013-2021 MultiMC Contributors
|
||||||
Contact: http://www.qt-project.org/legal
|
|
||||||
|
|
||||||
Licensed under LGPL v2.1
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
# libnbt++
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
## MinGW-w64 runtime (Windows)
|
||||||
|
|
||||||
|
Copyright (c) 2009, 2010, 2011, 2012, 2013 by the mingw-w64 project
|
||||||
|
|
||||||
|
This license has been certified as open source. It has also been designated
|
||||||
|
as GPL compatible by the Free Software Foundation (FSF).
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions in source code must retain the accompanying copyright
|
||||||
|
notice, this list of conditions, and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the accompanying
|
||||||
|
copyright notice, this list of conditions, and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
3. Names of the copyright holders must not be used to endorse or promote
|
||||||
|
products derived from this software without prior written permission
|
||||||
|
from the copyright holders.
|
||||||
|
4. The right to distribute this software or to use it for any purpose does
|
||||||
|
not give you the right to use Servicemarks (sm) or Trademarks (tm) of
|
||||||
|
the copyright holders. Use of them is covered by separate agreement
|
||||||
|
with the copyright holders.
|
||||||
|
5. If any files are modified, you must cause the modified files to carry
|
||||||
|
prominent notices stating that you changed the files and the date of
|
||||||
|
any change.
|
||||||
|
|
||||||
|
Disclaimer
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
|
||||||
|
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 COPYRIGHT HOLDERS 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.
|
||||||
|
|
||||||
|
Information on third party licenses used in MinGW-w64 can be found in its COPYING.MinGW-w64-runtime.txt.
|
||||||
|
|
||||||
|
## Qt 5/6
|
||||||
|
|
||||||
|
Copyright (C) 2022 The Qt Company Ltd and other contributors.
|
||||||
|
Contact: https://www.qt.io/licensing
|
||||||
|
|
||||||
|
Licensed under LGPL v3
|
||||||
|
|
||||||
|
## libnbt++
|
||||||
|
|
||||||
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
||||||
Copyright (C) 2013, 2015 ljfa-ag
|
Copyright (C) 2013, 2015 ljfa-ag
|
||||||
@ -77,7 +133,7 @@
|
|||||||
You should have received a copy of the GNU Lesser General Public License
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# rainbow (KGuiAddons)
|
## rainbow (KGuiAddons)
|
||||||
|
|
||||||
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
||||||
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
|
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
|
||||||
@ -100,25 +156,36 @@
|
|||||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
Boston, MA 02110-1301, USA.
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
# Hoedown
|
## cmark
|
||||||
|
|
||||||
Copyright (c) 2008, Natacha Porté
|
Copyright (c) 2014, John MacFarlane
|
||||||
Copyright (c) 2011, Vicent Martí
|
|
||||||
Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software for any
|
All rights reserved.
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
Redistribution and use in source and binary forms, with or without
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
modification, are permitted provided that the following conditions are met:
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
|
|
||||||
# Batch icon set
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||||
|
OWNER 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.
|
||||||
|
|
||||||
|
## Batch icon set
|
||||||
|
|
||||||
You are free to use Batch (the "icon set") or any part thereof (the "icons")
|
You are free to use Batch (the "icon set") or any part thereof (the "icons")
|
||||||
in any personal, open-source or commercial work without obligation of payment
|
in any personal, open-source or commercial work without obligation of payment
|
||||||
@ -134,7 +201,7 @@
|
|||||||
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
||||||
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
# Material Design Icons
|
## Material Design Icons
|
||||||
|
|
||||||
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
|
Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
|
||||||
with Reserved Font Name Material Design Icons.
|
with Reserved Font Name Material Design Icons.
|
||||||
@ -145,76 +212,82 @@
|
|||||||
This license is copied below, and is also available with a FAQ at:
|
This license is copied below, and is also available with a FAQ at:
|
||||||
http://scripts.sil.org/OFL
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
# Quazip
|
## Quazip
|
||||||
|
|
||||||
Copyright (C) 2005-2011 Sergey A. Tachenov
|
Copyright (C) 2005-2021 Sergey A. Tachenov
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify it
|
The QuaZip library is licensed under the GNU Lesser General Public
|
||||||
under the terms of the GNU Lesser General Public License as published by
|
License V2.1 plus a static linking exception.
|
||||||
the Free Software Foundation; either version 2 of the License, or (at
|
|
||||||
your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
The original ZIP/UNZIP package (MiniZip) is copyrighted by Gilles
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
Vollant and contributors, see quazip/(un)zip.h files for details.
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
|
Basically it's the zlib license.
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
STATIC LINKING EXCEPTION
|
||||||
along with this program; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
The copyright holders give you permission to link this library with
|
||||||
|
independent modules to produce an executable, regardless of the license
|
||||||
|
terms of these independent modules, and to copy and distribute the
|
||||||
|
resulting executable under terms of your choice, provided that you also
|
||||||
|
meet, for each linked independent module, the terms and conditions of
|
||||||
|
the license of that module. An independent module is a module which is
|
||||||
|
not derived from or based on this library. If you modify this library,
|
||||||
|
you must extend this exception to your version of the library.
|
||||||
|
|
||||||
See COPYING file for the full LGPL text.
|
See COPYING file for the full LGPL text.
|
||||||
|
|
||||||
Original ZIP package is copyrighted by Gilles Vollant, see
|
## launcher (`libraries/launcher`)
|
||||||
quazip/(un)zip.h files for details, basically it's zlib license.
|
|
||||||
|
|
||||||
# xz-minidec
|
PolyMC - Minecraft Launcher
|
||||||
|
Copyright (C) 2021-2022 PolyMC Contributors
|
||||||
|
|
||||||
XZ decompressor
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, version 3.
|
||||||
|
|
||||||
Authors: Lasse Collin <lasse.collin@tukaani.org>
|
This program is distributed in the hope that it will be useful,
|
||||||
Igor Pavlov <http://7-zip.org/>
|
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.
|
||||||
|
|
||||||
This file has been put into the public domain.
|
Linking this library statically or dynamically with other modules is
|
||||||
You can do whatever you want with this file.
|
making a combined work based on this library. Thus, the terms and
|
||||||
|
conditions of the GNU General Public License cover the whole
|
||||||
|
combination.
|
||||||
|
|
||||||
# ColumnResizer
|
As a special exception, the copyright holders of this library give
|
||||||
|
you permission to link this library with independent modules to
|
||||||
|
produce an executable, regardless of the license terms of these
|
||||||
|
independent modules, and to copy and distribute the resulting
|
||||||
|
executable under terms of your choice, provided that you also meet,
|
||||||
|
for each linked independent module, the terms and conditions of the
|
||||||
|
license of that module. An independent module is a module which is
|
||||||
|
not derived from or based on this library. If you modify this
|
||||||
|
library, you may extend this exception to your version of the
|
||||||
|
library, but you are not obliged to do so. If you do not wish to do
|
||||||
|
so, delete this exception statement from your version.
|
||||||
|
|
||||||
Copyright (c) 2011-2016 Aurélien Gâteau and contributors.
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
All rights reserved.
|
This file incorporates work covered by the following copyright and
|
||||||
|
permission notice:
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Copyright 2013-2021 MultiMC Contributors
|
||||||
modification, are permitted (subject to the limitations in the
|
|
||||||
disclaimer below) provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
notice, this list of conditions and the following disclaimer.
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
* The name of the contributors may not be used to endorse or
|
Unless required by applicable law or agreed to in writing, software
|
||||||
promote products derived from this software without specific prior
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
written permission.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
|
## lionshead
|
||||||
GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
|
|
||||||
HOLDERS 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 COPYRIGHT OWNER 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.
|
|
||||||
|
|
||||||
# lionshead
|
|
||||||
|
|
||||||
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
Code has been taken from https://github.com/natefoo/lionshead and loosely
|
||||||
translated to C++ laced with Qt.
|
translated to C++ laced with Qt.
|
||||||
@ -241,60 +314,26 @@
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
# optional-bare
|
## tomlplusplus
|
||||||
|
|
||||||
Code from https://github.com/martinmoene/optional-bare/
|
|
||||||
|
|
||||||
Boost Software License - Version 1.0 - August 17th, 2003
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person or organization
|
|
||||||
obtaining a copy of the software and accompanying documentation covered by
|
|
||||||
this license (the "Software") to use, reproduce, display, distribute,
|
|
||||||
execute, and transmit the Software, and to prepare derivative works of the
|
|
||||||
Software, and to permit third-parties to whom the Software is furnished to
|
|
||||||
do so, all subject to the following:
|
|
||||||
|
|
||||||
The copyright notices in the Software and this entire statement, including
|
|
||||||
the above license grant, this restriction and the following disclaimer,
|
|
||||||
must be included in all copies of the Software, in whole or in part, and
|
|
||||||
all derivative works of the Software, unless such copies or derivative
|
|
||||||
works are solely in the form of machine-executable object code generated by
|
|
||||||
a source language processor.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
||||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
||||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
# tomlc99
|
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 CK Tan
|
Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
|
||||||
https://github.com/cktan/tomlc99
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
||||||
in the Software without restriction, including without limitation the rights
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
||||||
copies or substantial portions of the Software.
|
Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|
||||||
# O2 (Katabasis fork)
|
## O2 (Katabasis fork)
|
||||||
|
|
||||||
Copyright (c) 2012, Akos Polster
|
Copyright (c) 2012, Akos Polster
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
@ -319,3 +358,96 @@
|
|||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
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
|
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.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
## Gamemode
|
||||||
|
|
||||||
|
Copyright (c) 2017-2022, Feral Interactive
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
* 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.
|
||||||
|
* Neither the name of Feral Interactive nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
|
||||||
|
|
||||||
|
## gulrak/filesystem
|
||||||
|
|
||||||
|
Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
## Breeze icons
|
||||||
|
|
||||||
|
Copyright (C) 2014 Uri Herrera <uri_herrera@nitrux.in> and others
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 3 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
## Oxygen Icons
|
||||||
|
|
||||||
|
The Oxygen Icon Theme
|
||||||
|
Copyright (C) 2007 Nuno Pinheiro <nuno@oxygen-icons.org>
|
||||||
|
Copyright (C) 2007 David Vignoni <david@icon-king.com>
|
||||||
|
Copyright (C) 2007 David Miller <miller@oxygen-icons.org>
|
||||||
|
Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
|
||||||
|
Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
|
||||||
|
Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
|
||||||
|
|
||||||
|
and others
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 3 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
123
README.md
123
README.md
@ -1,74 +1,103 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./program_info/polymc-header-black.svg#gh-light-mode-only" alt="PolyMC logo"/>
|
<picture>
|
||||||
<img src="./program_info/polymc-header.svg#gh-dark-mode-only" alt="PolyMC logo"/>
|
<source media="(prefers-color-scheme: dark)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo-darkmode.svg">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="/program_info/org.prismlauncher.PrismLauncher.logo.svg">
|
||||||
|
<img alt="Prism Launcher" src="/program_info/org.prismlauncher.PrismLauncher.logo.svg" width="40%">
|
||||||
|
</picture>
|
||||||
</p>
|
</p>
|
||||||
<br>
|
|
||||||
|
|
||||||
PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
|
<p align="center">
|
||||||
|
Prism Launcher is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.<br />
|
||||||
|
<br />This is a <b>fork</b> of the MultiMC Launcher and is <b>not</b> endorsed by it.
|
||||||
|
</p>
|
||||||
|
|
||||||
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC. The PolyMC community felt that the maintainer was not acting in the spirit of Free Software so this fork was made.
|
## Installation
|
||||||
<br>
|
|
||||||
|
|
||||||
# Installation
|
<a href="https://repology.org/project/prismlauncher/versions">
|
||||||
|
<img src="https://repology.org/badge/vertical-allrepos/prismlauncher.svg" alt="Packaging status" align="right">
|
||||||
|
</a>
|
||||||
|
|
||||||
- All downloads and instructions for PolyMC can be found [here](https://polymc.org/download/)
|
- All downloads and instructions for Prism Launcher can be found on our [Website](https://prismlauncher.org/download).
|
||||||
- Last build status: https://github.com/PolyMC/PolyMC/actions
|
- Last build status can be found in the [GitHub Actions](https://github.com/PrismLauncher/PrismLauncher/actions).
|
||||||
|
|
||||||
|
### Development Builds
|
||||||
|
|
||||||
## Development Builds
|
There are development builds available [here](https://github.com/PrismLauncher/PrismLauncher/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
||||||
|
|
||||||
There are per-commit development builds available [here](https://github.com/PolyMC/PolyMC/actions). These have debug information in the binaries, so their file sizes are relatively larger.
|
Prebuilt Development builds are provided for **Linux**, **Windows** and **macOS**.
|
||||||
Portable builds are provided for AppImage on Linux, Windows, and macOS.
|
|
||||||
|
|
||||||
For Debian and Arch, you can use these packages for the latest development versions:
|
For **Arch**, **Debian**, **Fedora**, **OpenSUSE (Tumbleweed)** and **Gentoo**, respectively, you can use these packages for the latest development versions:
|
||||||
[](https://aur.archlinux.org/packages/polymc-git/)
|
|
||||||
[](https://mpr.makedeb.org/packages/polymc-git)
|
|
||||||
For flatpak, you can use [flathub-beta](https://discourse.flathub.org/t/how-to-use-flathub-beta/2111)
|
|
||||||
|
|
||||||
# Help & Support
|
[](https://aur.archlinux.org/packages/prismlauncher-git) [](https://aur.archlinux.org/packages/prismlauncher-qt5-git) [](https://mpr.makedeb.org/packages/prismlauncher-git)<br />[](https://copr.fedorainfracloud.org/coprs/g3tchoo/prismlauncher/) [](https://build.opensuse.org/project/show/home:getchoo) [](https://packages.gentoo.org/packages/games-action/prismlauncher)
|
||||||
|
|
||||||
Feel free to create an issue if you need help. However, you might find it easier to ask in the Discord server.
|
These packages are also availiable to all the distributions based on the ones mentioned above.
|
||||||
|
|
||||||
[](https://discord.gg/xq7fxrgtMP)
|
## Community & Support
|
||||||
|
|
||||||
For people who don't want to use Discord, we have a Matrix Space which is bridged to the Discord server:
|
Feel free to create a GitHub issue if you find a bug or want to suggest a new feature. We have multiple community spaces where other community members can help you:
|
||||||
|
|
||||||
[](https://matrix.to/#/#polymc:polymc.org)
|
- **Our Discord server:**
|
||||||
|
|
||||||
If there are any issues with the space or you are using a client that does not support the feature here are the individual rooms:
|
[](https://prismlauncher.org/discord)
|
||||||
|
|
||||||
[](https://matrix.to/#/#support:polymc.org)
|
- **Our Matrix space:**
|
||||||
[](https://matrix.to/#/#discussion:polymc.org)
|
|
||||||
[](https://matrix.to/#/#development:polymc.org)
|
|
||||||
[](https://matrix.to/#/#news:polymc.org)
|
|
||||||
|
|
||||||
# Development
|
[](https://prismlauncher.org/matrix)
|
||||||
|
|
||||||
If you want to contribute to PolyMC you might find it useful to join our Discord Server or Matrix Space.
|
- **Our Subreddit:**
|
||||||
|
|
||||||
## Building
|
[](https://prismlauncher.org/reddit)
|
||||||
|
|
||||||
If you want to build PolyMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
|
||||||
|
|
||||||
## Code formatting
|
|
||||||
|
|
||||||
Just follow the existing formatting.
|
|
||||||
|
|
||||||
In general, in order of importance:
|
|
||||||
|
|
||||||
- Make sure your IDE is not messing up line endings or whitespace and avoid using linters.
|
|
||||||
- Prefer readability over dogma.
|
|
||||||
- Keep to the existing formatting.
|
|
||||||
- Indent with 4 space unless it's in a submodule.
|
|
||||||
- Keep lists (of arguments, parameters, initializers...) as lists, not paragraphs. It should either read from top to bottom, or left to right. Not both.
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
The translation effort for PolyMC is hosted on [Weblate](https://hosted.weblate.org/projects/polymc/polymc/) and information about translating PolyMC is available at https://github.com/PolyMC/Translations
|
The translation effort for PrismLauncher is hosted on [Weblate](https://hosted.weblate.org/projects/prismlauncher/launcher/) and information about translating Prism Launcher is available at <https://github.com/PrismLauncher/Translations>
|
||||||
|
|
||||||
## Download infomation
|
## Building
|
||||||
To modify download infomation or change packaging infomation send a pull request or issue to the website [Here](https://github.com/PolyMC/polymc.github.io/blob/master/src/download.md)
|
|
||||||
|
If you want to build Prism Launcher yourself, check the [Build Instructions](https://prismlauncher.org/wiki/development/build-instructions/).
|
||||||
|
|
||||||
|
## Sponsors & Partners
|
||||||
|
|
||||||
|
We thank all the wonderful backers over at Open Collective! Support Prism Launcher by [becoming a backer](https://opencollective.com/prismlauncher).
|
||||||
|
|
||||||
|
[](https://opencollective.com/prismlauncher#backers)
|
||||||
|
|
||||||
|
Thanks to JetBrains for providing us a few licenses for all their products, as part of their [Open Source program](https://www.jetbrains.com/opensource/).
|
||||||
|
|
||||||
|
[](https://www.jetbrains.com/opensource/)
|
||||||
|
|
||||||
|
Thanks to Weblate for hosting our translation efforts.
|
||||||
|
|
||||||
|
<a href="https://hosted.weblate.org/engage/prismlauncher/">
|
||||||
|
<img src="https://hosted.weblate.org/widgets/prismlauncher/-/open-graph.png" alt="Translation status" width="300" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
Thanks to Netlify for providing us their excellent web services, as part of their [Open Source program](https://www.netlify.com/open-source/).
|
||||||
|
|
||||||
|
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" /> </a>
|
||||||
|
|
||||||
|
Thanks to the awesome people over at [MacStadium](https://www.macstadium.com/), for providing M1-Macs for development purposes!
|
||||||
|
|
||||||
|
<a href="https://www.macstadium.com"><img src="https://uploads-ssl.webflow.com/5ac3c046c82724970fc60918/5c019d917bba312af7553b49_MacStadium-developerlogo.png" alt="Powered by MacStadium" width="300"></a>
|
||||||
|
|
||||||
## Forking/Redistributing/Custom builds policy
|
## Forking/Redistributing/Custom builds policy
|
||||||
|
|
||||||
Do whatever you want, we don't care. Just follow the license. If you have any questions about this feel free to ask in an issue.
|
We don't care what you do with your fork/custom build as long as you follow the terms of the [license](LICENSE) (this is a legal responsibility), and if you made code changes rather than just packaging a custom build, please do the following as a basic courtesy:
|
||||||
|
|
||||||
|
- Make it clear that your fork is not PrismLauncher and is not endorsed by or affiliated with the PrismLauncher project (<https://prismlauncher.org>).
|
||||||
|
- Go through [CMakeLists.txt](CMakeLists.txt) and change PrismLauncher's API keys to your own or set them to empty strings (`""`) to disable them (this way the program will still compile but the functionality requiring those keys will be disabled).
|
||||||
|
|
||||||
|
If you have any questions or want any clarification on the above conditions please make an issue and ask us.
|
||||||
|
|
||||||
|
Be aware that if you build this software without removing the provided API keys in [CMakeLists.txt](CMakeLists.txt) you are accepting the following terms and conditions:
|
||||||
|
|
||||||
|
- [Microsoft Identity Platform Terms of Use](https://docs.microsoft.com/en-us/legal/microsoft-identity-platform/terms-of-use)
|
||||||
|
- [CurseForge 3rd Party API Terms and Conditions](https://support.curseforge.com/en/support/solutions/articles/9000207405-curse-forge-3rd-party-api-terms-and-conditions)
|
||||||
|
|
||||||
|
If you do not agree with these terms and conditions, then remove the associated API keys from the [CMakeLists.txt](CMakeLists.txt) file by setting them to an empty string (`""`).
|
||||||
|
|
||||||
|
## License [](LICENSE)
|
||||||
|
|
||||||
|
All launcher code is available under the GPL-3.0-only license.
|
||||||
|
|
||||||
|
The logo and related assets are under the CC BY-SA 4.0 license.
|
||||||
|
@ -1,3 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "BuildConfig.h"
|
#include "BuildConfig.h"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
@ -7,12 +42,14 @@ Config::Config()
|
|||||||
{
|
{
|
||||||
// Name and copyright
|
// Name and copyright
|
||||||
LAUNCHER_NAME = "@Launcher_Name@";
|
LAUNCHER_NAME = "@Launcher_Name@";
|
||||||
|
LAUNCHER_APP_BINARY_NAME = "@Launcher_APP_BINARY_NAME@";
|
||||||
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
LAUNCHER_DISPLAYNAME = "@Launcher_DisplayName@";
|
||||||
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
LAUNCHER_COPYRIGHT = "@Launcher_Copyright@";
|
||||||
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
LAUNCHER_DOMAIN = "@Launcher_Domain@";
|
||||||
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
LAUNCHER_CONFIGFILE = "@Launcher_ConfigFile@";
|
||||||
LAUNCHER_GIT = "@Launcher_Git@";
|
LAUNCHER_GIT = "@Launcher_Git@";
|
||||||
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
LAUNCHER_DESKTOPFILENAME = "@Launcher_DesktopFileName@";
|
||||||
|
LAUNCHER_SVGFILENAME = "@Launcher_SVGFileName@";
|
||||||
|
|
||||||
USER_AGENT = "@Launcher_UserAgent@";
|
USER_AGENT = "@Launcher_UserAgent@";
|
||||||
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
USER_AGENT_UNCACHED = USER_AGENT + " (Uncached)";
|
||||||
@ -20,21 +57,38 @@ Config::Config()
|
|||||||
// Version information
|
// Version information
|
||||||
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
VERSION_MAJOR = @Launcher_VERSION_MAJOR@;
|
||||||
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
VERSION_MINOR = @Launcher_VERSION_MINOR@;
|
||||||
VERSION_HOTFIX = @Launcher_VERSION_HOTFIX@;
|
|
||||||
VERSION_BUILD = @Launcher_VERSION_BUILD@;
|
|
||||||
|
|
||||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||||
|
BUILD_DATE = "@Launcher_BUILD_TIMESTAMP@";
|
||||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||||
NOTIFICATION_URL = "@Launcher_NOTIFICATION_URL@";
|
|
||||||
FULL_VERSION_STR = "@Launcher_VERSION_MAJOR@.@Launcher_VERSION_MINOR@.@Launcher_VERSION_BUILD@";
|
MAC_SPARKLE_PUB_KEY = "@MACOSX_SPARKLE_UPDATE_PUBLIC_KEY@";
|
||||||
|
MAC_SPARKLE_APPCAST_URL = "@MACOSX_SPARKLE_UPDATE_FEED_URL@";
|
||||||
|
|
||||||
|
if (BUILD_PLATFORM == "macOS" && !MAC_SPARKLE_PUB_KEY.isEmpty() && !MAC_SPARKLE_APPCAST_URL.isEmpty())
|
||||||
|
{
|
||||||
|
UPDATER_ENABLED = true;
|
||||||
|
}
|
||||||
|
|
||||||
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
GIT_COMMIT = "@Launcher_GIT_COMMIT@";
|
||||||
|
GIT_TAG = "@Launcher_GIT_TAG@";
|
||||||
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
GIT_REFSPEC = "@Launcher_GIT_REFSPEC@";
|
||||||
|
|
||||||
|
// Assume that builds outside of Git repos are "stable"
|
||||||
|
if (GIT_REFSPEC == QStringLiteral("GITDIR-NOTFOUND")
|
||||||
|
|| GIT_TAG == QStringLiteral("GITDIR-NOTFOUND")
|
||||||
|
|| GIT_REFSPEC == QStringLiteral("")
|
||||||
|
|| GIT_TAG == QStringLiteral("GIT-NOTFOUND"))
|
||||||
|
{
|
||||||
|
GIT_REFSPEC = "refs/heads/stable";
|
||||||
|
GIT_TAG = versionString();
|
||||||
|
}
|
||||||
|
|
||||||
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
if (GIT_REFSPEC.startsWith("refs/heads/"))
|
||||||
{
|
{
|
||||||
VERSION_CHANNEL = GIT_REFSPEC;
|
VERSION_CHANNEL = GIT_REFSPEC;
|
||||||
VERSION_CHANNEL.remove("refs/heads/");
|
VERSION_CHANNEL.remove("refs/heads/");
|
||||||
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty() && VERSION_BUILD >= 0) {
|
if(!UPDATER_BASE.isEmpty() && !BUILD_PLATFORM.isEmpty()) {
|
||||||
UPDATER_ENABLED = true;
|
UPDATER_ENABLED = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,15 +98,15 @@ Config::Config()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
VERSION_CHANNEL = QObject::tr("unknown");
|
VERSION_CHANNEL = "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
VERSION_STR = "@Launcher_VERSION_STRING@";
|
|
||||||
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
|
||||||
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
NEWS_OPEN_URL = "@Launcher_NEWS_OPEN_URL@";
|
||||||
HELP_URL = "@Launcher_HELP_URL@";
|
HELP_URL = "@Launcher_HELP_URL@";
|
||||||
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
|
||||||
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
|
||||||
|
FLAME_API_KEY = "@Launcher_CURSEFORGE_API_KEY@";
|
||||||
META_URL = "@Launcher_META_URL@";
|
META_URL = "@Launcher_META_URL@";
|
||||||
|
|
||||||
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
BUG_TRACKER_URL = "@Launcher_BUG_TRACKER_URL@";
|
||||||
@ -62,20 +116,19 @@ Config::Config()
|
|||||||
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
SUBREDDIT_URL = "@Launcher_SUBREDDIT_URL@";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Config::versionString() const
|
||||||
|
{
|
||||||
|
return QString("%1.%2").arg(VERSION_MAJOR).arg(VERSION_MINOR);
|
||||||
|
}
|
||||||
|
|
||||||
QString Config::printableVersionString() const
|
QString Config::printableVersionString() const
|
||||||
{
|
{
|
||||||
QString vstr = QString("%1.%2.%3").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR), QString::number(VERSION_HOTFIX));
|
QString vstr = versionString();
|
||||||
|
|
||||||
// If the build is not a main release, append the channel
|
// If the build is not a main release, append the channel
|
||||||
if(VERSION_CHANNEL != "stable")
|
if(VERSION_CHANNEL != "stable" && GIT_TAG != vstr)
|
||||||
{
|
{
|
||||||
vstr += "-" + VERSION_CHANNEL;
|
vstr += "-" + VERSION_CHANNEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if a build number is set, also add it to the end
|
|
||||||
if(VERSION_BUILD >= 0)
|
|
||||||
{
|
|
||||||
vstr += "-" + QString::number(VERSION_BUILD);
|
|
||||||
}
|
|
||||||
return vstr;
|
return vstr;
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,64 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief The Config class holds all the build-time information passed from the build system.
|
* \brief The Config class holds all the build-time information passed from the build system.
|
||||||
*/
|
*/
|
||||||
class Config
|
class Config {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
Config();
|
Config();
|
||||||
QString LAUNCHER_NAME;
|
QString LAUNCHER_NAME;
|
||||||
|
QString LAUNCHER_APP_BINARY_NAME;
|
||||||
QString LAUNCHER_DISPLAYNAME;
|
QString LAUNCHER_DISPLAYNAME;
|
||||||
QString LAUNCHER_COPYRIGHT;
|
QString LAUNCHER_COPYRIGHT;
|
||||||
QString LAUNCHER_DOMAIN;
|
QString LAUNCHER_DOMAIN;
|
||||||
QString LAUNCHER_CONFIGFILE;
|
QString LAUNCHER_CONFIGFILE;
|
||||||
QString LAUNCHER_GIT;
|
QString LAUNCHER_GIT;
|
||||||
QString LAUNCHER_DESKTOPFILENAME;
|
QString LAUNCHER_DESKTOPFILENAME;
|
||||||
|
QString LAUNCHER_SVGFILENAME;
|
||||||
|
|
||||||
/// The major version number.
|
/// The major version number.
|
||||||
int VERSION_MAJOR;
|
int VERSION_MAJOR;
|
||||||
/// The minor version number.
|
/// The minor version number.
|
||||||
int VERSION_MINOR;
|
int VERSION_MINOR;
|
||||||
/// The hotfix number.
|
|
||||||
int VERSION_HOTFIX;
|
|
||||||
/// The build number.
|
|
||||||
int VERSION_BUILD;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version channel
|
* The version channel
|
||||||
@ -36,9 +71,17 @@ public:
|
|||||||
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||||
QString BUILD_PLATFORM;
|
QString BUILD_PLATFORM;
|
||||||
|
|
||||||
|
/// A string containing the build timestamp
|
||||||
|
QString BUILD_DATE;
|
||||||
|
|
||||||
/// URL for the updater's channel
|
/// URL for the updater's channel
|
||||||
QString UPDATER_BASE;
|
QString UPDATER_BASE;
|
||||||
|
|
||||||
|
/// The public key used to sign releases for the Sparkle updater appcast
|
||||||
|
QString MAC_SPARKLE_PUB_KEY;
|
||||||
|
|
||||||
|
/// URL for the Sparkle updater's appcast
|
||||||
|
QString MAC_SPARKLE_APPCAST_URL;
|
||||||
|
|
||||||
/// User-Agent to use.
|
/// User-Agent to use.
|
||||||
QString USER_AGENT;
|
QString USER_AGENT;
|
||||||
@ -46,22 +89,15 @@ public:
|
|||||||
/// User-Agent to use for uncached requests.
|
/// User-Agent to use for uncached requests.
|
||||||
QString USER_AGENT_UNCACHED;
|
QString USER_AGENT_UNCACHED;
|
||||||
|
|
||||||
|
|
||||||
/// URL for notifications
|
|
||||||
QString NOTIFICATION_URL;
|
|
||||||
|
|
||||||
/// Used for matching notifications
|
|
||||||
QString FULL_VERSION_STR;
|
|
||||||
|
|
||||||
/// The git commit hash of this build
|
/// The git commit hash of this build
|
||||||
QString GIT_COMMIT;
|
QString GIT_COMMIT;
|
||||||
|
|
||||||
|
/// The git tag of this build
|
||||||
|
QString GIT_TAG;
|
||||||
|
|
||||||
/// The git refspec of this build
|
/// The git refspec of this build
|
||||||
QString GIT_REFSPEC;
|
QString GIT_REFSPEC;
|
||||||
|
|
||||||
/// This is printed on start to standard output
|
|
||||||
QString VERSION_STR;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is used to fetch the news RSS feed.
|
* This is used to fetch the news RSS feed.
|
||||||
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
|
* It defaults in CMakeLists.txt to "https://multimc.org/rss.xml"
|
||||||
@ -88,6 +124,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
QString MSA_CLIENT_ID;
|
QString MSA_CLIENT_ID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client API key for CurseForge
|
||||||
|
*/
|
||||||
|
QString FLAME_API_KEY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metadata repository URL prefix
|
* Metadata repository URL prefix
|
||||||
*/
|
*/
|
||||||
@ -103,15 +144,29 @@ public:
|
|||||||
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
QString LIBRARY_BASE = "https://libraries.minecraft.net/";
|
||||||
QString AUTH_BASE = "https://authserver.mojang.com/";
|
QString AUTH_BASE = "https://authserver.mojang.com/";
|
||||||
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
QString IMGUR_BASE_URL = "https://api.imgur.com/3/";
|
||||||
QString FMLLIBS_BASE_URL = "https://files.polymc.org/fmllibs/";
|
QString FMLLIBS_BASE_URL = "https://files.prismlauncher.org/fmllibs/"; // FIXME: move into CMakeLists
|
||||||
QString TRANSLATIONS_BASE_URL = "https://i18n.polymc.org/";
|
QString TRANSLATIONS_BASE_URL = "https://i18n.prismlauncher.org/"; // FIXME: move into CMakeLists
|
||||||
|
|
||||||
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/";
|
||||||
|
|
||||||
QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/";
|
QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/";
|
||||||
|
|
||||||
QString ATL_DOWNLOAD_SERVER_URL = "https://download.nodecdn.net/containers/atl/";
|
QString ATL_DOWNLOAD_SERVER_URL = "https://download.nodecdn.net/containers/atl/";
|
||||||
|
QString ATL_API_BASE_URL = "https://api.atlauncher.com/v1/";
|
||||||
|
|
||||||
|
QString TECHNIC_API_BASE_URL = "https://api.technicpack.net/";
|
||||||
|
/**
|
||||||
|
* The build that is reported to the Technic API.
|
||||||
|
*/
|
||||||
|
QString TECHNIC_API_BUILD = "multimc";
|
||||||
|
|
||||||
|
QString MODRINTH_STAGING_URL = "https://staging-api.modrinth.com/v2";
|
||||||
|
QString MODRINTH_PROD_URL = "https://api.modrinth.com/v2";
|
||||||
|
QStringList MODRINTH_MRPACK_HOSTS{"cdn.modrinth.com", "github.com", "raw.githubusercontent.com", "gitlab.com"};
|
||||||
|
|
||||||
|
QString FLAME_BASE_URL = "https://api.curseforge.com/v1";
|
||||||
|
|
||||||
|
QString versionString() const;
|
||||||
/**
|
/**
|
||||||
* \brief Converts the Version to a string.
|
* \brief Converts the Version to a string.
|
||||||
* \return The version number in string format (major.minor.revision.build).
|
* \return The version number in string format (major.minor.revision.build).
|
||||||
@ -120,4 +175,3 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern const Config BuildConfig;
|
extern const Config BuildConfig;
|
||||||
|
|
||||||
|
@ -7,5 +7,5 @@ add_library(BuildConfig STATIC
|
|||||||
${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
|
${CMAKE_CURRENT_BINARY_DIR}/BuildConfig.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(BuildConfig Qt5::Core)
|
target_link_libraries(BuildConfig Qt${QT_VERSION_MAJOR}::Core)
|
||||||
target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
target_include_directories(BuildConfig PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
@ -1,786 +0,0 @@
|
|||||||
# - Functions to help assemble a standalone bundle application.
|
|
||||||
# A collection of CMake utility functions useful for dealing with .app
|
|
||||||
# bundles on the Mac and bundle-like directories on any OS.
|
|
||||||
#
|
|
||||||
# The following functions are provided by this module:
|
|
||||||
# fixup_bundle
|
|
||||||
# copy_and_fixup_bundle
|
|
||||||
# verify_app
|
|
||||||
# get_bundle_main_executable
|
|
||||||
# get_dotapp_dir
|
|
||||||
# get_bundle_and_executable
|
|
||||||
# get_bundle_all_executables
|
|
||||||
# get_item_key
|
|
||||||
# clear_bundle_keys
|
|
||||||
# set_bundle_key_values
|
|
||||||
# get_bundle_keys
|
|
||||||
# copy_resolved_item_into_bundle
|
|
||||||
# copy_resolved_framework_into_bundle
|
|
||||||
# fixup_bundle_item
|
|
||||||
# verify_bundle_prerequisites
|
|
||||||
# verify_bundle_symlinks
|
|
||||||
# Requires CMake 2.6 or greater because it uses function, break and
|
|
||||||
# PARENT_SCOPE. Also depends on GetPrerequisites.cmake.
|
|
||||||
#
|
|
||||||
# FIXUP_BUNDLE(<app> <libs> <dirs>)
|
|
||||||
# Fix up a bundle in-place and make it standalone, such that it can be
|
|
||||||
# drag-n-drop copied to another machine and run on that machine as long as all
|
|
||||||
# of the system libraries are compatible.
|
|
||||||
#
|
|
||||||
# If you pass plugins to fixup_bundle as the libs parameter, you should install
|
|
||||||
# them or copy them into the bundle before calling fixup_bundle. The "libs"
|
|
||||||
# parameter is a list of libraries that must be fixed up, but that cannot be
|
|
||||||
# determined by otool output analysis. (i.e., plugins)
|
|
||||||
#
|
|
||||||
# Gather all the keys for all the executables and libraries in a bundle, and
|
|
||||||
# then, for each key, copy each prerequisite into the bundle. Then fix each one
|
|
||||||
# up according to its own list of prerequisites.
|
|
||||||
#
|
|
||||||
# Then clear all the keys and call verify_app on the final bundle to ensure
|
|
||||||
# that it is truly standalone.
|
|
||||||
#
|
|
||||||
# COPY_AND_FIXUP_BUNDLE(<src> <dst> <libs> <dirs>)
|
|
||||||
# Makes a copy of the bundle <src> at location <dst> and then fixes up the
|
|
||||||
# new copied bundle in-place at <dst>...
|
|
||||||
#
|
|
||||||
# VERIFY_APP(<app>)
|
|
||||||
# Verifies that an application <app> appears valid based on running analysis
|
|
||||||
# tools on it. Calls "message(FATAL_ERROR" if the application is not verified.
|
|
||||||
#
|
|
||||||
# GET_BUNDLE_MAIN_EXECUTABLE(<bundle> <result_var>)
|
|
||||||
# The result will be the full path name of the bundle's main executable file
|
|
||||||
# or an "error:" prefixed string if it could not be determined.
|
|
||||||
#
|
|
||||||
# GET_DOTAPP_DIR(<exe> <dotapp_dir_var>)
|
|
||||||
# Returns the nearest parent dir whose name ends with ".app" given the full
|
|
||||||
# path to an executable. If there is no such parent dir, then simply return
|
|
||||||
# the dir containing the executable.
|
|
||||||
#
|
|
||||||
# The returned directory may or may not exist.
|
|
||||||
#
|
|
||||||
# GET_BUNDLE_AND_EXECUTABLE(<app> <bundle_var> <executable_var> <valid_var>)
|
|
||||||
# Takes either a ".app" directory name or the name of an executable
|
|
||||||
# nested inside a ".app" directory and returns the path to the ".app"
|
|
||||||
# directory in <bundle_var> and the path to its main executable in
|
|
||||||
# <executable_var>
|
|
||||||
#
|
|
||||||
# GET_BUNDLE_ALL_EXECUTABLES(<bundle> <exes_var>)
|
|
||||||
# Scans the given bundle recursively for all executable files and accumulates
|
|
||||||
# them into a variable.
|
|
||||||
#
|
|
||||||
# GET_ITEM_KEY(<item> <key_var>)
|
|
||||||
# Given a file (item) name, generate a key that should be unique considering
|
|
||||||
# the set of libraries that need copying or fixing up to make a bundle
|
|
||||||
# standalone. This is essentially the file name including extension with "."
|
|
||||||
# replaced by "_"
|
|
||||||
#
|
|
||||||
# This key is used as a prefix for CMake variables so that we can associate a
|
|
||||||
# set of variables with a given item based on its key.
|
|
||||||
#
|
|
||||||
# CLEAR_BUNDLE_KEYS(<keys_var>)
|
|
||||||
# Loop over the list of keys, clearing all the variables associated with each
|
|
||||||
# key. After the loop, clear the list of keys itself.
|
|
||||||
#
|
|
||||||
# Caller of get_bundle_keys should call clear_bundle_keys when done with list
|
|
||||||
# of keys.
|
|
||||||
#
|
|
||||||
# SET_BUNDLE_KEY_VALUES(<keys_var> <context> <item> <exepath> <dirs>
|
|
||||||
# <copyflag>)
|
|
||||||
# Add a key to the list (if necessary) for the given item. If added,
|
|
||||||
# also set all the variables associated with that key.
|
|
||||||
#
|
|
||||||
# GET_BUNDLE_KEYS(<app> <libs> <dirs> <keys_var>)
|
|
||||||
# Loop over all the executable and library files within the bundle (and given
|
|
||||||
# as extra <libs>) and accumulate a list of keys representing them. Set
|
|
||||||
# values associated with each key such that we can loop over all of them and
|
|
||||||
# copy prerequisite libs into the bundle and then do appropriate
|
|
||||||
# install_name_tool fixups.
|
|
||||||
#
|
|
||||||
# COPY_RESOLVED_ITEM_INTO_BUNDLE(<resolved_item> <resolved_embedded_item>)
|
|
||||||
# Copy a resolved item into the bundle if necessary. Copy is not necessary if
|
|
||||||
# the resolved_item is "the same as" the resolved_embedded_item.
|
|
||||||
#
|
|
||||||
# COPY_RESOLVED_FRAMEWORK_INTO_BUNDLE(<resolved_item> <resolved_embedded_item>)
|
|
||||||
# Copy a resolved framework into the bundle if necessary. Copy is not necessary
|
|
||||||
# if the resolved_item is "the same as" the resolved_embedded_item.
|
|
||||||
#
|
|
||||||
# By default, BU_COPY_FULL_FRAMEWORK_CONTENTS is not set. If you want full
|
|
||||||
# frameworks embedded in your bundles, set BU_COPY_FULL_FRAMEWORK_CONTENTS to
|
|
||||||
# ON before calling fixup_bundle. By default,
|
|
||||||
# COPY_RESOLVED_FRAMEWORK_INTO_BUNDLE copies the framework dylib itself plus
|
|
||||||
# the framework Resources directory.
|
|
||||||
#
|
|
||||||
# FIXUP_BUNDLE_ITEM(<resolved_embedded_item> <exepath> <dirs>)
|
|
||||||
# Get the direct/non-system prerequisites of the resolved embedded item. For
|
|
||||||
# each prerequisite, change the way it is referenced to the value of the
|
|
||||||
# _EMBEDDED_ITEM keyed variable for that prerequisite. (Most likely changing to
|
|
||||||
# an "@executable_path" style reference.)
|
|
||||||
#
|
|
||||||
# This function requires that the resolved_embedded_item be "inside" the bundle
|
|
||||||
# already. In other words, if you pass plugins to fixup_bundle as the libs
|
|
||||||
# parameter, you should install them or copy them into the bundle before
|
|
||||||
# calling fixup_bundle. The "libs" parameter is a list of libraries that must
|
|
||||||
# be fixed up, but that cannot be determined by otool output analysis. (i.e.,
|
|
||||||
# plugins)
|
|
||||||
#
|
|
||||||
# Also, change the id of the item being fixed up to its own _EMBEDDED_ITEM
|
|
||||||
# value.
|
|
||||||
#
|
|
||||||
# Accumulate changes in a local variable and make *one* call to
|
|
||||||
# install_name_tool at the end of the function with all the changes at once.
|
|
||||||
#
|
|
||||||
# If the BU_CHMOD_BUNDLE_ITEMS variable is set then bundle items will be
|
|
||||||
# marked writable before install_name_tool tries to change them.
|
|
||||||
#
|
|
||||||
# VERIFY_BUNDLE_PREREQUISITES(<bundle> <result_var> <info_var>)
|
|
||||||
# Verifies that the sum of all prerequisites of all files inside the bundle
|
|
||||||
# are contained within the bundle or are "system" libraries, presumed to exist
|
|
||||||
# everywhere.
|
|
||||||
#
|
|
||||||
# VERIFY_BUNDLE_SYMLINKS(<bundle> <result_var> <info_var>)
|
|
||||||
# Verifies that any symlinks found in the bundle point to other files that are
|
|
||||||
# already also in the bundle... Anything that points to an external file causes
|
|
||||||
# this function to fail the verification.
|
|
||||||
|
|
||||||
#=============================================================================
|
|
||||||
# Copyright 2008-2009 Kitware, Inc.
|
|
||||||
#
|
|
||||||
# Distributed under the OSI-approved BSD License (the "License");
|
|
||||||
# see accompanying file Copyright.txt for details.
|
|
||||||
#
|
|
||||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
||||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the License for more information.
|
|
||||||
#=============================================================================
|
|
||||||
# (To distribute this file outside of CMake, substitute the full
|
|
||||||
# License text for the above reference.)
|
|
||||||
|
|
||||||
# The functions defined in this file depend on the get_prerequisites function
|
|
||||||
# (and possibly others) found in:
|
|
||||||
#
|
|
||||||
get_filename_component(BundleUtilities_cmake_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
|
||||||
include("${BundleUtilities_cmake_dir}/GetPrerequisites.cmake")
|
|
||||||
|
|
||||||
|
|
||||||
function(get_bundle_main_executable bundle result_var)
|
|
||||||
set(result "error: '${bundle}/Contents/Info.plist' file does not exist")
|
|
||||||
|
|
||||||
if(EXISTS "${bundle}/Contents/Info.plist")
|
|
||||||
set(result "error: no CFBundleExecutable in '${bundle}/Contents/Info.plist' file")
|
|
||||||
set(line_is_main_executable 0)
|
|
||||||
set(bundle_executable "")
|
|
||||||
|
|
||||||
# Read Info.plist as a list of lines:
|
|
||||||
#
|
|
||||||
set(eol_char "E")
|
|
||||||
file(READ "${bundle}/Contents/Info.plist" info_plist)
|
|
||||||
string(REGEX REPLACE ";" "\\\\;" info_plist "${info_plist}")
|
|
||||||
string(REGEX REPLACE "\n" "${eol_char};" info_plist "${info_plist}")
|
|
||||||
|
|
||||||
# Scan the lines for "<key>CFBundleExecutable</key>" - the line after that
|
|
||||||
# is the name of the main executable.
|
|
||||||
#
|
|
||||||
foreach(line ${info_plist})
|
|
||||||
if(line_is_main_executable)
|
|
||||||
string(REGEX REPLACE "^.*<string>(.*)</string>.*$" "\\1" bundle_executable "${line}")
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(line MATCHES "^.*<key>CFBundleExecutable</key>.*$")
|
|
||||||
set(line_is_main_executable 1)
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(NOT "${bundle_executable}" STREQUAL "")
|
|
||||||
if(EXISTS "${bundle}/Contents/MacOS/${bundle_executable}")
|
|
||||||
set(result "${bundle}/Contents/MacOS/${bundle_executable}")
|
|
||||||
else()
|
|
||||||
|
|
||||||
# Ultimate goal:
|
|
||||||
# If not in "Contents/MacOS" then scan the bundle for matching files. If
|
|
||||||
# there is only one executable file that matches, then use it, otherwise
|
|
||||||
# it's an error...
|
|
||||||
#
|
|
||||||
#file(GLOB_RECURSE file_list "${bundle}/${bundle_executable}")
|
|
||||||
|
|
||||||
# But for now, pragmatically, it's an error. Expect the main executable
|
|
||||||
# for the bundle to be in Contents/MacOS, it's an error if it's not:
|
|
||||||
#
|
|
||||||
set(result "error: '${bundle}/Contents/MacOS/${bundle_executable}' does not exist")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
#
|
|
||||||
# More inclusive technique... (This one would work on Windows and Linux
|
|
||||||
# too, if a developer followed the typical Mac bundle naming convention...)
|
|
||||||
#
|
|
||||||
# If there is no Info.plist file, try to find an executable with the same
|
|
||||||
# base name as the .app directory:
|
|
||||||
#
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${result_var} "${result}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(get_dotapp_dir exe dotapp_dir_var)
|
|
||||||
set(s "${exe}")
|
|
||||||
|
|
||||||
if(s MATCHES "^.*/.*\\.app/.*$")
|
|
||||||
# If there is a ".app" parent directory,
|
|
||||||
# ascend until we hit it:
|
|
||||||
# (typical of a Mac bundle executable)
|
|
||||||
#
|
|
||||||
set(done 0)
|
|
||||||
while(NOT ${done})
|
|
||||||
get_filename_component(snamewe "${s}" NAME_WE)
|
|
||||||
get_filename_component(sname "${s}" NAME)
|
|
||||||
get_filename_component(sdir "${s}" PATH)
|
|
||||||
set(s "${sdir}")
|
|
||||||
if(sname MATCHES "\\.app$")
|
|
||||||
set(done 1)
|
|
||||||
set(dotapp_dir "${sdir}/${sname}")
|
|
||||||
endif()
|
|
||||||
endwhile()
|
|
||||||
else()
|
|
||||||
# Otherwise use a directory containing the exe
|
|
||||||
# (typical of a non-bundle executable on Mac, Windows or Linux)
|
|
||||||
#
|
|
||||||
is_file_executable("${s}" is_executable)
|
|
||||||
if(is_executable)
|
|
||||||
get_filename_component(sdir "${s}" PATH)
|
|
||||||
set(dotapp_dir "${sdir}")
|
|
||||||
else()
|
|
||||||
set(dotapp_dir "${s}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
set(${dotapp_dir_var} "${dotapp_dir}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(get_bundle_and_executable app bundle_var executable_var valid_var)
|
|
||||||
set(valid 0)
|
|
||||||
|
|
||||||
if(EXISTS "${app}")
|
|
||||||
# Is it a directory ending in .app?
|
|
||||||
if(IS_DIRECTORY "${app}")
|
|
||||||
if(app MATCHES "\\.app$")
|
|
||||||
get_bundle_main_executable("${app}" executable)
|
|
||||||
if(EXISTS "${app}" AND EXISTS "${executable}")
|
|
||||||
set(${bundle_var} "${app}" PARENT_SCOPE)
|
|
||||||
set(${executable_var} "${executable}" PARENT_SCOPE)
|
|
||||||
set(valid 1)
|
|
||||||
#message(STATUS "info: handled .app directory case...")
|
|
||||||
else()
|
|
||||||
message(STATUS "warning: *NOT* handled - .app directory case...")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(STATUS "warning: *NOT* handled - directory but not .app case...")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
# Is it an executable file?
|
|
||||||
is_file_executable("${app}" is_executable)
|
|
||||||
if(is_executable)
|
|
||||||
get_dotapp_dir("${app}" dotapp_dir)
|
|
||||||
if(EXISTS "${dotapp_dir}")
|
|
||||||
set(${bundle_var} "${dotapp_dir}" PARENT_SCOPE)
|
|
||||||
set(${executable_var} "${app}" PARENT_SCOPE)
|
|
||||||
set(valid 1)
|
|
||||||
#message(STATUS "info: handled executable file in .app dir case...")
|
|
||||||
else()
|
|
||||||
get_filename_component(app_dir "${app}" PATH)
|
|
||||||
set(${bundle_var} "${app_dir}" PARENT_SCOPE)
|
|
||||||
set(${executable_var} "${app}" PARENT_SCOPE)
|
|
||||||
set(valid 1)
|
|
||||||
#message(STATUS "info: handled executable file in any dir case...")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(STATUS "warning: *NOT* handled - not .app dir, not executable file...")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(STATUS "warning: *NOT* handled - directory/file ${app} does not exist...")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT valid)
|
|
||||||
set(${bundle_var} "error: not a bundle" PARENT_SCOPE)
|
|
||||||
set(${executable_var} "error: not a bundle" PARENT_SCOPE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${valid_var} ${valid} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(get_bundle_all_executables bundle exes_var)
|
|
||||||
set(exes "")
|
|
||||||
|
|
||||||
file(GLOB_RECURSE file_list "${bundle}/*")
|
|
||||||
foreach(f ${file_list})
|
|
||||||
is_file_executable("${f}" is_executable)
|
|
||||||
if(is_executable)
|
|
||||||
set(exes ${exes} "${f}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
set(${exes_var} "${exes}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(get_item_key item key_var)
|
|
||||||
get_filename_component(item_name "${item}" NAME)
|
|
||||||
if(WIN32)
|
|
||||||
string(TOLOWER "${item_name}" item_name)
|
|
||||||
endif()
|
|
||||||
string(REGEX REPLACE "\\." "_" ${key_var} "${item_name}")
|
|
||||||
set(${key_var} ${${key_var}} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(clear_bundle_keys keys_var)
|
|
||||||
foreach(key ${${keys_var}})
|
|
||||||
set(${key}_ITEM PARENT_SCOPE)
|
|
||||||
set(${key}_RESOLVED_ITEM PARENT_SCOPE)
|
|
||||||
set(${key}_DEFAULT_EMBEDDED_PATH PARENT_SCOPE)
|
|
||||||
set(${key}_EMBEDDED_ITEM PARENT_SCOPE)
|
|
||||||
set(${key}_RESOLVED_EMBEDDED_ITEM PARENT_SCOPE)
|
|
||||||
set(${key}_COPYFLAG PARENT_SCOPE)
|
|
||||||
endforeach()
|
|
||||||
set(${keys_var} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(set_bundle_key_values keys_var context item exepath dirs copyflag)
|
|
||||||
get_filename_component(item_name "${item}" NAME)
|
|
||||||
|
|
||||||
get_item_key("${item}" key)
|
|
||||||
|
|
||||||
list(LENGTH ${keys_var} length_before)
|
|
||||||
gp_append_unique(${keys_var} "${key}")
|
|
||||||
list(LENGTH ${keys_var} length_after)
|
|
||||||
|
|
||||||
if(NOT length_before EQUAL length_after)
|
|
||||||
gp_resolve_item("${context}" "${item}" "${exepath}" "${dirs}" resolved_item)
|
|
||||||
|
|
||||||
gp_item_default_embedded_path("${item}" default_embedded_path)
|
|
||||||
|
|
||||||
if(item MATCHES "[^/]+\\.framework/")
|
|
||||||
# For frameworks, construct the name under the embedded path from the
|
|
||||||
# opening "${item_name}.framework/" to the closing "/${item_name}":
|
|
||||||
#
|
|
||||||
string(REGEX REPLACE "^.*(${item_name}.framework/.*/?${item_name}).*$" "${default_embedded_path}/\\1" embedded_item "${item}")
|
|
||||||
else()
|
|
||||||
# For other items, just use the same name as the original, but in the
|
|
||||||
# embedded path:
|
|
||||||
#
|
|
||||||
set(embedded_item "${default_embedded_path}/${item_name}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Replace @executable_path and resolve ".." references:
|
|
||||||
#
|
|
||||||
string(REPLACE "@executable_path" "${exepath}" resolved_embedded_item "${embedded_item}")
|
|
||||||
get_filename_component(resolved_embedded_item "${resolved_embedded_item}" ABSOLUTE)
|
|
||||||
|
|
||||||
# *But* -- if we are not copying, then force resolved_embedded_item to be
|
|
||||||
# the same as resolved_item. In the case of multiple executables in the
|
|
||||||
# original bundle, using the default_embedded_path results in looking for
|
|
||||||
# the resolved executable next to the main bundle executable. This is here
|
|
||||||
# so that exes in the other sibling directories (like "bin") get fixed up
|
|
||||||
# properly...
|
|
||||||
#
|
|
||||||
if(NOT copyflag)
|
|
||||||
set(resolved_embedded_item "${resolved_item}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${keys_var} ${${keys_var}} PARENT_SCOPE)
|
|
||||||
set(${key}_ITEM "${item}" PARENT_SCOPE)
|
|
||||||
set(${key}_RESOLVED_ITEM "${resolved_item}" PARENT_SCOPE)
|
|
||||||
set(${key}_DEFAULT_EMBEDDED_PATH "${default_embedded_path}" PARENT_SCOPE)
|
|
||||||
set(${key}_EMBEDDED_ITEM "${embedded_item}" PARENT_SCOPE)
|
|
||||||
set(${key}_RESOLVED_EMBEDDED_ITEM "${resolved_embedded_item}" PARENT_SCOPE)
|
|
||||||
set(${key}_COPYFLAG "${copyflag}" PARENT_SCOPE)
|
|
||||||
else()
|
|
||||||
#message("warning: item key '${key}' already in the list, subsequent references assumed identical to first")
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(get_bundle_keys app libs dirs keys_var)
|
|
||||||
set(${keys_var} PARENT_SCOPE)
|
|
||||||
|
|
||||||
get_bundle_and_executable("${app}" bundle executable valid)
|
|
||||||
if(valid)
|
|
||||||
# Always use the exepath of the main bundle executable for @executable_path
|
|
||||||
# replacements:
|
|
||||||
#
|
|
||||||
get_filename_component(exepath "${executable}" PATH)
|
|
||||||
|
|
||||||
# But do fixups on all executables in the bundle:
|
|
||||||
#
|
|
||||||
get_bundle_all_executables("${bundle}" exes)
|
|
||||||
|
|
||||||
# For each extra lib, accumulate a key as well and then also accumulate
|
|
||||||
# any of its prerequisites. (Extra libs are typically dynamically loaded
|
|
||||||
# plugins: libraries that are prerequisites for full runtime functionality
|
|
||||||
# but that do not show up in otool -L output...)
|
|
||||||
#
|
|
||||||
foreach(lib ${libs})
|
|
||||||
set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0)
|
|
||||||
|
|
||||||
set(prereqs "")
|
|
||||||
get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}")
|
|
||||||
foreach(pr ${prereqs})
|
|
||||||
set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1)
|
|
||||||
endforeach()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# For each executable found in the bundle, accumulate keys as we go.
|
|
||||||
# The list of keys should be complete when all prerequisites of all
|
|
||||||
# binaries in the bundle have been analyzed.
|
|
||||||
#
|
|
||||||
foreach(exe ${exes})
|
|
||||||
# Add the exe itself to the keys:
|
|
||||||
#
|
|
||||||
set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0)
|
|
||||||
|
|
||||||
# Add each prerequisite to the keys:
|
|
||||||
#
|
|
||||||
set(prereqs "")
|
|
||||||
get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}")
|
|
||||||
foreach(pr ${prereqs})
|
|
||||||
set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1)
|
|
||||||
endforeach()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
# Propagate values to caller's scope:
|
|
||||||
#
|
|
||||||
set(${keys_var} ${${keys_var}} PARENT_SCOPE)
|
|
||||||
foreach(key ${${keys_var}})
|
|
||||||
set(${key}_ITEM "${${key}_ITEM}" PARENT_SCOPE)
|
|
||||||
set(${key}_RESOLVED_ITEM "${${key}_RESOLVED_ITEM}" PARENT_SCOPE)
|
|
||||||
set(${key}_DEFAULT_EMBEDDED_PATH "${${key}_DEFAULT_EMBEDDED_PATH}" PARENT_SCOPE)
|
|
||||||
set(${key}_EMBEDDED_ITEM "${${key}_EMBEDDED_ITEM}" PARENT_SCOPE)
|
|
||||||
set(${key}_RESOLVED_EMBEDDED_ITEM "${${key}_RESOLVED_EMBEDDED_ITEM}" PARENT_SCOPE)
|
|
||||||
set(${key}_COPYFLAG "${${key}_COPYFLAG}" PARENT_SCOPE)
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
|
|
||||||
if(WIN32)
|
|
||||||
# ignore case on Windows
|
|
||||||
string(TOLOWER "${resolved_item}" resolved_item_compare)
|
|
||||||
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
|
|
||||||
else()
|
|
||||||
set(resolved_item_compare "${resolved_item}")
|
|
||||||
set(resolved_embedded_item_compare "${resolved_embedded_item}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
|
|
||||||
message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
|
|
||||||
else()
|
|
||||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
|
|
||||||
if(UNIX AND NOT APPLE)
|
|
||||||
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(copy_resolved_framework_into_bundle resolved_item resolved_embedded_item)
|
|
||||||
if(WIN32)
|
|
||||||
# ignore case on Windows
|
|
||||||
string(TOLOWER "${resolved_item}" resolved_item_compare)
|
|
||||||
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
|
|
||||||
else()
|
|
||||||
set(resolved_item_compare "${resolved_item}")
|
|
||||||
set(resolved_embedded_item_compare "${resolved_embedded_item}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
|
|
||||||
message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
|
|
||||||
else()
|
|
||||||
if(BU_COPY_FULL_FRAMEWORK_CONTENTS)
|
|
||||||
# Full Framework (everything):
|
|
||||||
get_filename_component(resolved_dir "${resolved_item}" PATH)
|
|
||||||
get_filename_component(resolved_dir "${resolved_dir}/../.." ABSOLUTE)
|
|
||||||
get_filename_component(resolved_embedded_dir "${resolved_embedded_item}" PATH)
|
|
||||||
get_filename_component(resolved_embedded_dir "${resolved_embedded_dir}/../.." ABSOLUTE)
|
|
||||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_dir}' '${resolved_embedded_dir}'")
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_dir}" "${resolved_embedded_dir}")
|
|
||||||
else()
|
|
||||||
# Framework lib itself:
|
|
||||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
|
|
||||||
|
|
||||||
# Plus Resources, if they exist:
|
|
||||||
string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Resources" resolved_resources "${resolved_item}")
|
|
||||||
string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Resources" resolved_embedded_resources "${resolved_embedded_item}")
|
|
||||||
if(EXISTS "${resolved_resources}")
|
|
||||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_resources}' '${resolved_embedded_resources}'")
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_resources}" "${resolved_embedded_resources}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if(UNIX AND NOT APPLE)
|
|
||||||
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(fixup_bundle_item resolved_embedded_item exepath dirs)
|
|
||||||
# This item's key is "ikey":
|
|
||||||
#
|
|
||||||
get_item_key("${resolved_embedded_item}" ikey)
|
|
||||||
|
|
||||||
# Ensure the item is "inside the .app bundle" -- it should not be fixed up if
|
|
||||||
# it is not in the .app bundle... Otherwise, we'll modify files in the build
|
|
||||||
# tree, or in other varied locations around the file system, with our call to
|
|
||||||
# install_name_tool. Make sure that doesn't happen here:
|
|
||||||
#
|
|
||||||
get_dotapp_dir("${exepath}" exe_dotapp_dir)
|
|
||||||
string(LENGTH "${exe_dotapp_dir}/" exe_dotapp_dir_length)
|
|
||||||
string(LENGTH "${resolved_embedded_item}" resolved_embedded_item_length)
|
|
||||||
set(path_too_short 0)
|
|
||||||
set(is_embedded 0)
|
|
||||||
if(${resolved_embedded_item_length} LESS ${exe_dotapp_dir_length})
|
|
||||||
set(path_too_short 1)
|
|
||||||
endif()
|
|
||||||
if(NOT path_too_short)
|
|
||||||
string(SUBSTRING "${resolved_embedded_item}" 0 ${exe_dotapp_dir_length} item_substring)
|
|
||||||
if("${exe_dotapp_dir}/" STREQUAL "${item_substring}")
|
|
||||||
set(is_embedded 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if(NOT is_embedded)
|
|
||||||
message(" exe_dotapp_dir/='${exe_dotapp_dir}/'")
|
|
||||||
message(" item_substring='${item_substring}'")
|
|
||||||
message(" resolved_embedded_item='${resolved_embedded_item}'")
|
|
||||||
message("")
|
|
||||||
message("Install or copy the item into the bundle before calling fixup_bundle.")
|
|
||||||
message("Or maybe there's a typo or incorrect path in one of the args to fixup_bundle?")
|
|
||||||
message("")
|
|
||||||
message(FATAL_ERROR "cannot fixup an item that is not in the bundle...")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(prereqs "")
|
|
||||||
get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}")
|
|
||||||
|
|
||||||
set(changes "")
|
|
||||||
|
|
||||||
foreach(pr ${prereqs})
|
|
||||||
# Each referenced item's key is "rkey" in the loop:
|
|
||||||
#
|
|
||||||
get_item_key("${pr}" rkey)
|
|
||||||
|
|
||||||
if(NOT "${${rkey}_EMBEDDED_ITEM}" STREQUAL "")
|
|
||||||
set(changes ${changes} "-change" "${pr}" "${${rkey}_EMBEDDED_ITEM}")
|
|
||||||
else()
|
|
||||||
message("warning: unexpected reference to '${pr}'")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(BU_CHMOD_BUNDLE_ITEMS)
|
|
||||||
execute_process(COMMAND chmod u+w "${resolved_embedded_item}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Change this item's id and all of its references in one call
|
|
||||||
# to install_name_tool:
|
|
||||||
#
|
|
||||||
execute_process(COMMAND install_name_tool
|
|
||||||
${changes} -id "${${ikey}_EMBEDDED_ITEM}" "${resolved_embedded_item}"
|
|
||||||
)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(fixup_bundle app libs dirs)
|
|
||||||
message(STATUS "fixup_bundle")
|
|
||||||
message(STATUS " app='${app}'")
|
|
||||||
message(STATUS " libs='${libs}'")
|
|
||||||
message(STATUS " dirs='${dirs}'")
|
|
||||||
|
|
||||||
get_bundle_and_executable("${app}" bundle executable valid)
|
|
||||||
if(valid)
|
|
||||||
get_filename_component(exepath "${executable}" PATH)
|
|
||||||
|
|
||||||
message(STATUS "fixup_bundle: preparing...")
|
|
||||||
get_bundle_keys("${app}" "${libs}" "${dirs}" keys)
|
|
||||||
|
|
||||||
message(STATUS "fixup_bundle: copying...")
|
|
||||||
list(LENGTH keys n)
|
|
||||||
math(EXPR n ${n}*2)
|
|
||||||
|
|
||||||
set(i 0)
|
|
||||||
foreach(key ${keys})
|
|
||||||
math(EXPR i ${i}+1)
|
|
||||||
if(${${key}_COPYFLAG})
|
|
||||||
message(STATUS "${i}/${n}: copying '${${key}_RESOLVED_ITEM}'")
|
|
||||||
else()
|
|
||||||
message(STATUS "${i}/${n}: *NOT* copying '${${key}_RESOLVED_ITEM}'")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(show_status 0)
|
|
||||||
if(show_status)
|
|
||||||
message(STATUS "key='${key}'")
|
|
||||||
message(STATUS "item='${${key}_ITEM}'")
|
|
||||||
message(STATUS "resolved_item='${${key}_RESOLVED_ITEM}'")
|
|
||||||
message(STATUS "default_embedded_path='${${key}_DEFAULT_EMBEDDED_PATH}'")
|
|
||||||
message(STATUS "embedded_item='${${key}_EMBEDDED_ITEM}'")
|
|
||||||
message(STATUS "resolved_embedded_item='${${key}_RESOLVED_EMBEDDED_ITEM}'")
|
|
||||||
message(STATUS "copyflag='${${key}_COPYFLAG}'")
|
|
||||||
message(STATUS "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(${${key}_COPYFLAG})
|
|
||||||
set(item "${${key}_ITEM}")
|
|
||||||
if(item MATCHES "[^/]+\\.framework/")
|
|
||||||
copy_resolved_framework_into_bundle("${${key}_RESOLVED_ITEM}"
|
|
||||||
"${${key}_RESOLVED_EMBEDDED_ITEM}")
|
|
||||||
else()
|
|
||||||
copy_resolved_item_into_bundle("${${key}_RESOLVED_ITEM}"
|
|
||||||
"${${key}_RESOLVED_EMBEDDED_ITEM}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
message(STATUS "fixup_bundle: fixing...")
|
|
||||||
foreach(key ${keys})
|
|
||||||
math(EXPR i ${i}+1)
|
|
||||||
if(APPLE)
|
|
||||||
message(STATUS "${i}/${n}: fixing up '${${key}_RESOLVED_EMBEDDED_ITEM}'")
|
|
||||||
fixup_bundle_item("${${key}_RESOLVED_EMBEDDED_ITEM}" "${exepath}" "${dirs}")
|
|
||||||
else()
|
|
||||||
message(STATUS "${i}/${n}: fix-up not required on this platform '${${key}_RESOLVED_EMBEDDED_ITEM}'")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
message(STATUS "fixup_bundle: cleaning up...")
|
|
||||||
clear_bundle_keys(keys)
|
|
||||||
|
|
||||||
message(STATUS "fixup_bundle: verifying...")
|
|
||||||
verify_app("${app}")
|
|
||||||
else()
|
|
||||||
message(SEND_ERROR "error: fixup_bundle: not a valid bundle")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS "fixup_bundle: done")
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(copy_and_fixup_bundle src dst libs dirs)
|
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${src}" "${dst}")
|
|
||||||
fixup_bundle("${dst}" "${libs}" "${dirs}")
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(verify_bundle_prerequisites bundle result_var info_var)
|
|
||||||
set(result 1)
|
|
||||||
set(info "")
|
|
||||||
set(count 0)
|
|
||||||
|
|
||||||
get_bundle_main_executable("${bundle}" main_bundle_exe)
|
|
||||||
|
|
||||||
file(GLOB_RECURSE file_list "${bundle}/*")
|
|
||||||
foreach(f ${file_list})
|
|
||||||
is_file_executable("${f}" is_executable)
|
|
||||||
if(is_executable)
|
|
||||||
get_filename_component(exepath "${f}" PATH)
|
|
||||||
math(EXPR count "${count} + 1")
|
|
||||||
|
|
||||||
message(STATUS "executable file ${count}: ${f}")
|
|
||||||
|
|
||||||
set(prereqs "")
|
|
||||||
get_prerequisites("${f}" prereqs 1 1 "${exepath}" "")
|
|
||||||
|
|
||||||
# On the Mac,
|
|
||||||
# "embedded" and "system" prerequisites are fine... anything else means
|
|
||||||
# the bundle's prerequisites are not verified (i.e., the bundle is not
|
|
||||||
# really "standalone")
|
|
||||||
#
|
|
||||||
# On Windows (and others? Linux/Unix/...?)
|
|
||||||
# "local" and "system" prereqs are fine...
|
|
||||||
#
|
|
||||||
set(external_prereqs "")
|
|
||||||
|
|
||||||
foreach(p ${prereqs})
|
|
||||||
set(p_type "")
|
|
||||||
gp_file_type("${f}" "${p}" p_type)
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
if(NOT "${p_type}" STREQUAL "embedded" AND NOT "${p_type}" STREQUAL "system")
|
|
||||||
set(external_prereqs ${external_prereqs} "${p}")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(NOT "${p_type}" STREQUAL "local" AND NOT "${p_type}" STREQUAL "system")
|
|
||||||
set(external_prereqs ${external_prereqs} "${p}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(external_prereqs)
|
|
||||||
# Found non-system/somehow-unacceptable prerequisites:
|
|
||||||
set(result 0)
|
|
||||||
set(info ${info} "external prerequisites found:\nf='${f}'\nexternal_prereqs='${external_prereqs}'\n")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(result)
|
|
||||||
set(info "Verified ${count} executable files in '${bundle}'")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${result_var} "${result}" PARENT_SCOPE)
|
|
||||||
set(${info_var} "${info}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(verify_bundle_symlinks bundle result_var info_var)
|
|
||||||
set(result 1)
|
|
||||||
set(info "")
|
|
||||||
set(count 0)
|
|
||||||
|
|
||||||
# TODO: implement this function for real...
|
|
||||||
# Right now, it is just a stub that verifies unconditionally...
|
|
||||||
|
|
||||||
set(${result_var} "${result}" PARENT_SCOPE)
|
|
||||||
set(${info_var} "${info}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(verify_app app)
|
|
||||||
set(verified 0)
|
|
||||||
set(info "")
|
|
||||||
|
|
||||||
get_bundle_and_executable("${app}" bundle executable valid)
|
|
||||||
|
|
||||||
message(STATUS "===========================================================================")
|
|
||||||
message(STATUS "Analyzing app='${app}'")
|
|
||||||
message(STATUS "bundle='${bundle}'")
|
|
||||||
message(STATUS "executable='${executable}'")
|
|
||||||
message(STATUS "valid='${valid}'")
|
|
||||||
|
|
||||||
# Verify that the bundle does not have any "external" prerequisites:
|
|
||||||
#
|
|
||||||
verify_bundle_prerequisites("${bundle}" verified info)
|
|
||||||
message(STATUS "verified='${verified}'")
|
|
||||||
message(STATUS "info='${info}'")
|
|
||||||
message(STATUS "")
|
|
||||||
|
|
||||||
if(verified)
|
|
||||||
# Verify that the bundle does not have any symlinks to external files:
|
|
||||||
#
|
|
||||||
verify_bundle_symlinks("${bundle}" verified info)
|
|
||||||
message(STATUS "verified='${verified}'")
|
|
||||||
message(STATUS "info='${info}'")
|
|
||||||
message(STATUS "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT verified)
|
|
||||||
message(FATAL_ERROR "error: verify_app failed")
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
100
cmake/ECMQueryQt.cmake
Normal file
100
cmake/ECMQueryQt.cmake
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2014 Rohan Garg <rohan16garg@gmail.com>
|
||||||
|
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||||
|
# SPDX-FileCopyrightText: 2014-2016 Aleix Pol <aleixpol@kde.org>
|
||||||
|
# SPDX-FileCopyrightText: 2017 Friedrich W. H. Kossebau <kossebau@kde.org>
|
||||||
|
# SPDX-FileCopyrightText: 2022 Ahmad Samir <a.samir78@gmail.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
#[=======================================================================[.rst:
|
||||||
|
ECMQueryQt
|
||||||
|
---------------
|
||||||
|
This module can be used to query the installation paths used by Qt.
|
||||||
|
|
||||||
|
For Qt5 this uses ``qmake``, and for Qt6 this used ``qtpaths`` (the latter has built-in
|
||||||
|
support to query the paths of a target platform when cross-compiling).
|
||||||
|
|
||||||
|
This module defines the following function:
|
||||||
|
::
|
||||||
|
|
||||||
|
ecm_query_qt(<result_variable> <qt_variable> [TRY])
|
||||||
|
|
||||||
|
Passing ``TRY`` will result in the method not making the build fail if the executable
|
||||||
|
used for querying has not been found, but instead simply print a warning message and
|
||||||
|
return an empty string.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
.. code-block:: cmake
|
||||||
|
|
||||||
|
include(ECMQueryQt)
|
||||||
|
ecm_query_qt(bin_dir QT_INSTALL_BINS)
|
||||||
|
|
||||||
|
If the call succeeds ``${bin_dir}`` will be set to ``<prefix>/path/to/bin/dir`` (e.g.
|
||||||
|
``/usr/lib64/qt/bin/``).
|
||||||
|
|
||||||
|
Since: 5.93
|
||||||
|
#]=======================================================================]
|
||||||
|
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/QtVersionOption.cmake)
|
||||||
|
include(CheckLanguage)
|
||||||
|
check_language(CXX)
|
||||||
|
if (CMAKE_CXX_COMPILER)
|
||||||
|
# Enable the CXX language to let CMake look for config files in library dirs.
|
||||||
|
# See: https://gitlab.kitware.com/cmake/cmake/-/issues/23266
|
||||||
|
enable_language(CXX)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (QT_MAJOR_VERSION STREQUAL "5")
|
||||||
|
# QUIET to accommodate the TRY option
|
||||||
|
find_package(Qt${QT_MAJOR_VERSION}Core QUIET)
|
||||||
|
if(TARGET Qt5::qmake)
|
||||||
|
get_target_property(_qmake_executable_default Qt5::qmake LOCATION)
|
||||||
|
|
||||||
|
set(QUERY_EXECUTABLE ${_qmake_executable_default}
|
||||||
|
CACHE FILEPATH "Location of the Qt5 qmake executable")
|
||||||
|
set(_exec_name_text "Qt5 qmake")
|
||||||
|
set(_cli_option "-query")
|
||||||
|
endif()
|
||||||
|
elseif(QT_MAJOR_VERSION STREQUAL "6")
|
||||||
|
# QUIET to accommodate the TRY option
|
||||||
|
find_package(Qt6 COMPONENTS CoreTools QUIET CONFIG)
|
||||||
|
if (TARGET Qt6::qtpaths)
|
||||||
|
get_target_property(_qtpaths_executable Qt6::qtpaths LOCATION)
|
||||||
|
|
||||||
|
set(QUERY_EXECUTABLE ${_qtpaths_executable}
|
||||||
|
CACHE FILEPATH "Location of the Qt6 qtpaths executable")
|
||||||
|
set(_exec_name_text "Qt6 qtpaths")
|
||||||
|
set(_cli_option "--query")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(ecm_query_qt result_variable qt_variable)
|
||||||
|
set(options TRY)
|
||||||
|
set(oneValueArgs)
|
||||||
|
set(multiValueArgs)
|
||||||
|
|
||||||
|
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if(NOT QUERY_EXECUTABLE)
|
||||||
|
if(ARGS_TRY)
|
||||||
|
set(${result_variable} "" PARENT_SCOPE)
|
||||||
|
message(STATUS "No ${_exec_name_text} executable found. Can't check ${qt_variable}")
|
||||||
|
return()
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "No ${_exec_name_text} executable found. Can't check ${qt_variable} as required")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${QUERY_EXECUTABLE} ${_cli_option} "${qt_variable}"
|
||||||
|
RESULT_VARIABLE return_code
|
||||||
|
OUTPUT_VARIABLE output
|
||||||
|
)
|
||||||
|
if(return_code EQUAL 0)
|
||||||
|
string(STRIP "${output}" output)
|
||||||
|
file(TO_CMAKE_PATH "${output}" output_path)
|
||||||
|
set(${result_variable} "${output_path}" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
message(WARNING "Failed call: ${_command} \"${qt_variable}\"")
|
||||||
|
message(FATAL_ERROR "${_exec_name_text} call failed: ${return_code}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
@ -3,7 +3,7 @@
|
|||||||
# These functions force a re-configure on each git commit so that you can
|
# These functions force a re-configure on each git commit so that you can
|
||||||
# trust the values of the variables in your build system.
|
# trust the values of the variables in your build system.
|
||||||
#
|
#
|
||||||
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
|
# get_git_head_revision(<refspecvar> <hashvar> [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR])
|
||||||
#
|
#
|
||||||
# Returns the refspec and sha hash of the current head revision
|
# Returns the refspec and sha hash of the current head revision
|
||||||
#
|
#
|
||||||
@ -12,20 +12,33 @@
|
|||||||
# Returns the results of git describe on the source tree, and adjusting
|
# Returns the results of git describe on the source tree, and adjusting
|
||||||
# the output so that it tests false if an error occurs.
|
# the output so that it tests false if an error occurs.
|
||||||
#
|
#
|
||||||
|
# git_describe_working_tree(<var> [<additional arguments to git describe> ...])
|
||||||
|
#
|
||||||
|
# Returns the results of git describe on the working tree (--dirty option),
|
||||||
|
# and adjusting the output so that it tests false if an error occurs.
|
||||||
|
#
|
||||||
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
|
||||||
#
|
#
|
||||||
# Returns the results of git describe --exact-match on the source tree,
|
# Returns the results of git describe --exact-match on the source tree,
|
||||||
# and adjusting the output so that it tests false if there was no exact
|
# and adjusting the output so that it tests false if there was no exact
|
||||||
# matching tag.
|
# matching tag.
|
||||||
#
|
#
|
||||||
|
# git_local_changes(<var>)
|
||||||
|
#
|
||||||
|
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
|
||||||
|
# Uses the return code of "git diff-index --quiet HEAD --".
|
||||||
|
# Does not regard untracked files.
|
||||||
|
#
|
||||||
# Requires CMake 2.6 or newer (uses the 'function' command)
|
# Requires CMake 2.6 or newer (uses the 'function' command)
|
||||||
#
|
#
|
||||||
# Original Author:
|
# Original Author:
|
||||||
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
|
# 2009-2020 Ryan Pavlik <ryan.pavlik@gmail.com> <abiryan@ryand.net>
|
||||||
# http://academic.cleardefinition.com
|
# http://academic.cleardefinition.com
|
||||||
# Iowa State University HCI Graduate Program/VRAC
|
|
||||||
#
|
#
|
||||||
# Copyright Iowa State University 2009-2010.
|
# Copyright 2009-2013, Iowa State University.
|
||||||
|
# Copyright 2013-2020, Ryan Pavlik
|
||||||
|
# Copyright 2013-2020, Contributors
|
||||||
|
# SPDX-License-Identifier: BSL-1.0
|
||||||
# Distributed under the Boost Software License, Version 1.0.
|
# Distributed under the Boost Software License, Version 1.0.
|
||||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
# http://www.boost.org/LICENSE_1_0.txt)
|
# http://www.boost.org/LICENSE_1_0.txt)
|
||||||
@ -39,45 +52,124 @@ set(__get_git_revision_description YES)
|
|||||||
# to find the path to this module rather than the path to a calling list file
|
# to find the path to this module rather than the path to a calling list file
|
||||||
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
|
||||||
|
|
||||||
function(get_git_head_revision _refspecvar _hashvar)
|
# Function _git_find_closest_git_dir finds the next closest .git directory
|
||||||
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
# that is part of any directory in the path defined by _start_dir.
|
||||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
# The result is returned in the parent scope variable whose name is passed
|
||||||
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
|
# as variable _git_dir_var. If no .git directory can be found, the
|
||||||
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
|
# function returns an empty string via _git_dir_var.
|
||||||
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
|
#
|
||||||
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
|
# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and
|
||||||
|
# neither foo nor bar contain a file/directory .git. This wil return
|
||||||
|
# C:/bla/.git
|
||||||
|
#
|
||||||
|
function(_git_find_closest_git_dir _start_dir _git_dir_var)
|
||||||
|
set(cur_dir "${_start_dir}")
|
||||||
|
set(git_dir "${_start_dir}/.git")
|
||||||
|
while(NOT EXISTS "${git_dir}")
|
||||||
|
# .git dir not found, search parent directories
|
||||||
|
set(git_previous_parent "${cur_dir}")
|
||||||
|
get_filename_component(cur_dir "${cur_dir}" DIRECTORY)
|
||||||
|
if(cur_dir STREQUAL git_previous_parent)
|
||||||
# We have reached the root directory, we are not in git
|
# We have reached the root directory, we are not in git
|
||||||
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
set(${_git_dir_var}
|
||||||
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
|
""
|
||||||
|
PARENT_SCOPE)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
|
set(git_dir "${cur_dir}/.git")
|
||||||
endwhile()
|
endwhile()
|
||||||
# check if this is a submodule
|
set(${_git_dir_var}
|
||||||
|
"${git_dir}"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(get_git_head_revision _refspecvar _hashvar)
|
||||||
|
_git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR)
|
||||||
|
|
||||||
|
if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR")
|
||||||
|
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE)
|
||||||
|
else()
|
||||||
|
set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE)
|
||||||
|
endif()
|
||||||
|
if(NOT "${GIT_DIR}" STREQUAL "")
|
||||||
|
file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}"
|
||||||
|
"${GIT_DIR}")
|
||||||
|
if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR)
|
||||||
|
# We've gone above the CMake root dir.
|
||||||
|
set(GIT_DIR "")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if("${GIT_DIR}" STREQUAL "")
|
||||||
|
set(${_refspecvar}
|
||||||
|
"GITDIR-NOTFOUND"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
set(${_hashvar}
|
||||||
|
"GITDIR-NOTFOUND"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check if the current source dir is a git submodule or a worktree.
|
||||||
|
# In both cases .git is a file instead of a directory.
|
||||||
|
#
|
||||||
if(NOT IS_DIRECTORY ${GIT_DIR})
|
if(NOT IS_DIRECTORY ${GIT_DIR})
|
||||||
|
# The following git command will return a non empty string that
|
||||||
|
# points to the super project working tree if the current
|
||||||
|
# source dir is inside a git submodule.
|
||||||
|
# Otherwise the command will return an empty string.
|
||||||
|
#
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" rev-parse
|
||||||
|
--show-superproject-working-tree
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
OUTPUT_VARIABLE out
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
if(NOT "${out}" STREQUAL "")
|
||||||
|
# If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule
|
||||||
file(READ ${GIT_DIR} submodule)
|
file(READ ${GIT_DIR} submodule)
|
||||||
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
|
string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE
|
||||||
|
${submodule})
|
||||||
|
string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE)
|
||||||
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
|
||||||
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
|
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE}
|
||||||
|
ABSOLUTE)
|
||||||
|
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||||
|
else()
|
||||||
|
# GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree
|
||||||
|
file(READ ${GIT_DIR} worktree_ref)
|
||||||
|
# The .git directory contains a path to the worktree information directory
|
||||||
|
# inside the parent git repo of the worktree.
|
||||||
|
#
|
||||||
|
string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir
|
||||||
|
${worktree_ref})
|
||||||
|
string(STRIP ${git_worktree_dir} git_worktree_dir)
|
||||||
|
_git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR)
|
||||||
|
set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD")
|
||||||
endif()
|
endif()
|
||||||
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
|
||||||
if(NOT EXISTS "${GIT_DATA}")
|
if(NOT EXISTS "${GIT_DATA}")
|
||||||
file(MAKE_DIRECTORY "${GIT_DATA}")
|
file(MAKE_DIRECTORY "${GIT_DATA}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT EXISTS "${GIT_DIR}/HEAD")
|
if(NOT EXISTS "${HEAD_SOURCE_FILE}")
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
set(HEAD_FILE "${GIT_DATA}/HEAD")
|
||||||
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
|
configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY)
|
||||||
|
|
||||||
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
|
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
|
||||||
"${GIT_DATA}/grabRef.cmake"
|
"${GIT_DATA}/grabRef.cmake" @ONLY)
|
||||||
@ONLY)
|
|
||||||
include("${GIT_DATA}/grabRef.cmake")
|
include("${GIT_DATA}/grabRef.cmake")
|
||||||
|
|
||||||
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
|
set(${_refspecvar}
|
||||||
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
|
"${HEAD_REF}"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
set(${_hashvar}
|
||||||
|
"${HEAD_HASH}"
|
||||||
|
PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(git_describe _var)
|
function(git_describe _var)
|
||||||
@ -86,11 +178,15 @@ function(git_describe _var)
|
|||||||
endif()
|
endif()
|
||||||
get_git_head_revision(refspec hash)
|
get_git_head_revision(refspec hash)
|
||||||
if(NOT GIT_FOUND)
|
if(NOT GIT_FOUND)
|
||||||
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
|
set(${_var}
|
||||||
|
"GIT-NOTFOUND"
|
||||||
|
PARENT_SCOPE)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
if(NOT hash)
|
if(NOT hash)
|
||||||
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
|
set(${_var}
|
||||||
|
"HEAD-HASH-NOTFOUND"
|
||||||
|
PARENT_SCOPE)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -104,27 +200,85 @@ function(git_describe _var)
|
|||||||
|
|
||||||
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
#message(STATUS "Arguments to execute_process: ${ARGN}")
|
||||||
|
|
||||||
execute_process(COMMAND
|
execute_process(
|
||||||
"${GIT_EXECUTABLE}"
|
COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN}
|
||||||
describe
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
${hash}
|
RESULT_VARIABLE res
|
||||||
${ARGN}
|
OUTPUT_VARIABLE out
|
||||||
WORKING_DIRECTORY
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
RESULT_VARIABLE
|
|
||||||
res
|
|
||||||
OUTPUT_VARIABLE
|
|
||||||
out
|
|
||||||
ERROR_QUIET
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
if(NOT res EQUAL 0)
|
if(NOT res EQUAL 0)
|
||||||
set(out "${out}-${res}-NOTFOUND")
|
set(out "${out}-${res}-NOTFOUND")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(${_var} "${out}" PARENT_SCOPE)
|
set(${_var}
|
||||||
|
"${out}"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(git_describe_working_tree _var)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
find_package(Git QUIET)
|
||||||
|
endif()
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
set(${_var}
|
||||||
|
"GIT-NOTFOUND"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN}
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
RESULT_VARIABLE res
|
||||||
|
OUTPUT_VARIABLE out
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
if(NOT res EQUAL 0)
|
||||||
|
set(out "${out}-${res}-NOTFOUND")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(${_var}
|
||||||
|
"${out}"
|
||||||
|
PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(git_get_exact_tag _var)
|
function(git_get_exact_tag _var)
|
||||||
git_describe(out --exact-match ${ARGN})
|
git_describe(out --exact-match ${ARGN})
|
||||||
set(${_var} "${out}" PARENT_SCOPE)
|
set(${_var}
|
||||||
|
"${out}"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(git_local_changes _var)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
find_package(Git QUIET)
|
||||||
|
endif()
|
||||||
|
get_git_head_revision(refspec hash)
|
||||||
|
if(NOT GIT_FOUND)
|
||||||
|
set(${_var}
|
||||||
|
"GIT-NOTFOUND"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
if(NOT hash)
|
||||||
|
set(${_var}
|
||||||
|
"HEAD-HASH-NOTFOUND"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD --
|
||||||
|
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
RESULT_VARIABLE res
|
||||||
|
OUTPUT_VARIABLE out
|
||||||
|
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||||
|
if(res EQUAL 0)
|
||||||
|
set(${_var}
|
||||||
|
"CLEAN"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(${_var}
|
||||||
|
"DIRTY"
|
||||||
|
PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
@ -8,10 +8,12 @@
|
|||||||
# http://academic.cleardefinition.com
|
# http://academic.cleardefinition.com
|
||||||
# Iowa State University HCI Graduate Program/VRAC
|
# Iowa State University HCI Graduate Program/VRAC
|
||||||
#
|
#
|
||||||
# Copyright Iowa State University 2009-2010.
|
# Copyright 2009-2012, Iowa State University
|
||||||
|
# Copyright 2011-2015, Contributors
|
||||||
# Distributed under the Boost Software License, Version 1.0.
|
# Distributed under the Boost Software License, Version 1.0.
|
||||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
# http://www.boost.org/LICENSE_1_0.txt)
|
# http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
# SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
set(HEAD_HASH)
|
set(HEAD_HASH)
|
||||||
|
|
||||||
|
@ -1,902 +0,0 @@
|
|||||||
# - Functions to analyze and list executable file prerequisites.
|
|
||||||
# This module provides functions to list the .dll, .dylib or .so
|
|
||||||
# files that an executable or shared library file depends on. (Its
|
|
||||||
# prerequisites.)
|
|
||||||
#
|
|
||||||
# It uses various tools to obtain the list of required shared library files:
|
|
||||||
# dumpbin (Windows)
|
|
||||||
# objdump (MinGW on Windows)
|
|
||||||
# ldd (Linux/Unix)
|
|
||||||
# otool (Mac OSX)
|
|
||||||
# The following functions are provided by this module:
|
|
||||||
# get_prerequisites
|
|
||||||
# list_prerequisites
|
|
||||||
# list_prerequisites_by_glob
|
|
||||||
# gp_append_unique
|
|
||||||
# is_file_executable
|
|
||||||
# gp_item_default_embedded_path
|
|
||||||
# (projects can override with gp_item_default_embedded_path_override)
|
|
||||||
# gp_resolve_item
|
|
||||||
# (projects can override with gp_resolve_item_override)
|
|
||||||
# gp_resolved_file_type
|
|
||||||
# (projects can override with gp_resolved_file_type_override)
|
|
||||||
# gp_file_type
|
|
||||||
# Requires CMake 2.6 or greater because it uses function, break, return and
|
|
||||||
# PARENT_SCOPE.
|
|
||||||
#
|
|
||||||
# GET_PREREQUISITES(<target> <prerequisites_var> <exclude_system> <recurse>
|
|
||||||
# <exepath> <dirs>)
|
|
||||||
# Get the list of shared library files required by <target>. The list in
|
|
||||||
# the variable named <prerequisites_var> should be empty on first entry to
|
|
||||||
# this function. On exit, <prerequisites_var> will contain the list of
|
|
||||||
# required shared library files.
|
|
||||||
#
|
|
||||||
# <target> is the full path to an executable file. <prerequisites_var> is the
|
|
||||||
# name of a CMake variable to contain the results. <exclude_system> must be 0
|
|
||||||
# or 1 indicating whether to include or exclude "system" prerequisites. If
|
|
||||||
# <recurse> is set to 1 all prerequisites will be found recursively, if set to
|
|
||||||
# 0 only direct prerequisites are listed. <exepath> is the path to the top
|
|
||||||
# level executable used for @executable_path replacment on the Mac. <dirs> is
|
|
||||||
# a list of paths where libraries might be found: these paths are searched
|
|
||||||
# first when a target without any path info is given. Then standard system
|
|
||||||
# locations are also searched: PATH, Framework locations, /usr/lib...
|
|
||||||
#
|
|
||||||
# LIST_PREREQUISITES(<target> [<recurse> [<exclude_system> [<verbose>]]])
|
|
||||||
# Print a message listing the prerequisites of <target>.
|
|
||||||
#
|
|
||||||
# <target> is the name of a shared library or executable target or the full
|
|
||||||
# path to a shared library or executable file. If <recurse> is set to 1 all
|
|
||||||
# prerequisites will be found recursively, if set to 0 only direct
|
|
||||||
# prerequisites are listed. <exclude_system> must be 0 or 1 indicating whether
|
|
||||||
# to include or exclude "system" prerequisites. With <verbose> set to 0 only
|
|
||||||
# the full path names of the prerequisites are printed, set to 1 extra
|
|
||||||
# informatin will be displayed.
|
|
||||||
#
|
|
||||||
# LIST_PREREQUISITES_BY_GLOB(<glob_arg> <glob_exp>)
|
|
||||||
# Print the prerequisites of shared library and executable files matching a
|
|
||||||
# globbing pattern. <glob_arg> is GLOB or GLOB_RECURSE and <glob_exp> is a
|
|
||||||
# globbing expression used with "file(GLOB" or "file(GLOB_RECURSE" to retrieve
|
|
||||||
# a list of matching files. If a matching file is executable, its prerequisites
|
|
||||||
# are listed.
|
|
||||||
#
|
|
||||||
# Any additional (optional) arguments provided are passed along as the
|
|
||||||
# optional arguments to the list_prerequisites calls.
|
|
||||||
#
|
|
||||||
# GP_APPEND_UNIQUE(<list_var> <value>)
|
|
||||||
# Append <value> to the list variable <list_var> only if the value is not
|
|
||||||
# already in the list.
|
|
||||||
#
|
|
||||||
# IS_FILE_EXECUTABLE(<file> <result_var>)
|
|
||||||
# Return 1 in <result_var> if <file> is a binary executable, 0 otherwise.
|
|
||||||
#
|
|
||||||
# GP_ITEM_DEFAULT_EMBEDDED_PATH(<item> <default_embedded_path_var>)
|
|
||||||
# Return the path that others should refer to the item by when the item
|
|
||||||
# is embedded inside a bundle.
|
|
||||||
#
|
|
||||||
# Override on a per-project basis by providing a project-specific
|
|
||||||
# gp_item_default_embedded_path_override function.
|
|
||||||
#
|
|
||||||
# GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>)
|
|
||||||
# Resolve an item into an existing full path file.
|
|
||||||
#
|
|
||||||
# Override on a per-project basis by providing a project-specific
|
|
||||||
# gp_resolve_item_override function.
|
|
||||||
#
|
|
||||||
# GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>)
|
|
||||||
# Return the type of <file> with respect to <original_file>. String
|
|
||||||
# describing type of prerequisite is returned in variable named <type_var>.
|
|
||||||
#
|
|
||||||
# Use <exepath> and <dirs> if necessary to resolve non-absolute <file>
|
|
||||||
# values -- but only for non-embedded items.
|
|
||||||
#
|
|
||||||
# Possible types are:
|
|
||||||
# system
|
|
||||||
# local
|
|
||||||
# embedded
|
|
||||||
# other
|
|
||||||
# Override on a per-project basis by providing a project-specific
|
|
||||||
# gp_resolved_file_type_override function.
|
|
||||||
#
|
|
||||||
# GP_FILE_TYPE(<original_file> <file> <type_var>)
|
|
||||||
# Return the type of <file> with respect to <original_file>. String
|
|
||||||
# describing type of prerequisite is returned in variable named <type_var>.
|
|
||||||
#
|
|
||||||
# Possible types are:
|
|
||||||
# system
|
|
||||||
# local
|
|
||||||
# embedded
|
|
||||||
# other
|
|
||||||
|
|
||||||
#=============================================================================
|
|
||||||
# Copyright 2008-2009 Kitware, Inc.
|
|
||||||
#
|
|
||||||
# Distributed under the OSI-approved BSD License (the "License");
|
|
||||||
# see accompanying file Copyright.txt for details.
|
|
||||||
#
|
|
||||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
||||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the License for more information.
|
|
||||||
#=============================================================================
|
|
||||||
# (To distribute this file outside of CMake, substitute the full
|
|
||||||
# License text for the above reference.)
|
|
||||||
|
|
||||||
function(gp_append_unique list_var value)
|
|
||||||
set(contains 0)
|
|
||||||
|
|
||||||
foreach(item ${${list_var}})
|
|
||||||
if("${item}" STREQUAL "${value}")
|
|
||||||
set(contains 1)
|
|
||||||
break()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(NOT contains)
|
|
||||||
set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE)
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(is_file_executable file result_var)
|
|
||||||
#
|
|
||||||
# A file is not executable until proven otherwise:
|
|
||||||
#
|
|
||||||
set(${result_var} 0 PARENT_SCOPE)
|
|
||||||
|
|
||||||
get_filename_component(file_full "${file}" ABSOLUTE)
|
|
||||||
string(TOLOWER "${file_full}" file_full_lower)
|
|
||||||
|
|
||||||
# If file name ends in .exe on Windows, *assume* executable:
|
|
||||||
#
|
|
||||||
if(WIN32 AND NOT UNIX)
|
|
||||||
if("${file_full_lower}" MATCHES "\\.exe$")
|
|
||||||
set(${result_var} 1 PARENT_SCOPE)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# A clause could be added here that uses output or return value of dumpbin
|
|
||||||
# to determine ${result_var}. In 99%+? practical cases, the exe name
|
|
||||||
# match will be sufficient...
|
|
||||||
#
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Use the information returned from the Unix shell command "file" to
|
|
||||||
# determine if ${file_full} should be considered an executable file...
|
|
||||||
#
|
|
||||||
# If the file command's output contains "executable" and does *not* contain
|
|
||||||
# "text" then it is likely an executable suitable for prerequisite analysis
|
|
||||||
# via the get_prerequisites macro.
|
|
||||||
#
|
|
||||||
if(UNIX)
|
|
||||||
if(NOT file_cmd)
|
|
||||||
find_program(file_cmd "file")
|
|
||||||
mark_as_advanced(file_cmd)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(file_cmd)
|
|
||||||
execute_process(COMMAND "${file_cmd}" "${file_full}"
|
|
||||||
OUTPUT_VARIABLE file_ov
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
|
|
||||||
# Replace the name of the file in the output with a placeholder token
|
|
||||||
# (the string " _file_full_ ") so that just in case the path name of
|
|
||||||
# the file contains the word "text" or "executable" we are not fooled
|
|
||||||
# into thinking "the wrong thing" because the file name matches the
|
|
||||||
# other 'file' command output we are looking for...
|
|
||||||
#
|
|
||||||
string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}")
|
|
||||||
string(TOLOWER "${file_ov}" file_ov)
|
|
||||||
|
|
||||||
#message(STATUS "file_ov='${file_ov}'")
|
|
||||||
if("${file_ov}" MATCHES "executable")
|
|
||||||
#message(STATUS "executable!")
|
|
||||||
if("${file_ov}" MATCHES "text")
|
|
||||||
#message(STATUS "but text, so *not* a binary executable!")
|
|
||||||
else()
|
|
||||||
set(${result_var} 1 PARENT_SCOPE)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Also detect position independent executables on Linux,
|
|
||||||
# where "file" gives "shared object ... (uses shared libraries)"
|
|
||||||
if("${file_ov}" MATCHES "shared object.*\(uses shared libs\)")
|
|
||||||
set(${result_var} 1 PARENT_SCOPE)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# "file" version 5.22 does not print "(used shared libraries)"
|
|
||||||
# but uses "interpreter"
|
|
||||||
if("${file_ov}" MATCHES "shared object.*interpreter")
|
|
||||||
set(${result_var} 1 PARENT_SCOPE)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
else()
|
|
||||||
message(STATUS "warning: No 'file' command, skipping execute_process...")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(gp_item_default_embedded_path item default_embedded_path_var)
|
|
||||||
|
|
||||||
# On Windows and Linux, "embed" prerequisites in the same directory
|
|
||||||
# as the executable by default:
|
|
||||||
#
|
|
||||||
set(path "@executable_path")
|
|
||||||
set(overridden 0)
|
|
||||||
|
|
||||||
# On the Mac, relative to the executable depending on the type
|
|
||||||
# of the thing we are embedding:
|
|
||||||
#
|
|
||||||
if(APPLE)
|
|
||||||
#
|
|
||||||
# The assumption here is that all executables in the bundle will be
|
|
||||||
# in same-level-directories inside the bundle. The parent directory
|
|
||||||
# of an executable inside the bundle should be MacOS or a sibling of
|
|
||||||
# MacOS and all embedded paths returned from here will begin with
|
|
||||||
# "@executable_path/../" and will work from all executables in all
|
|
||||||
# such same-level-directories inside the bundle.
|
|
||||||
#
|
|
||||||
|
|
||||||
# By default, embed things right next to the main bundle executable:
|
|
||||||
#
|
|
||||||
set(path "@executable_path/../../Contents/MacOS")
|
|
||||||
|
|
||||||
# Embed .dylibs right next to the main bundle executable:
|
|
||||||
#
|
|
||||||
if(item MATCHES "\\.dylib$")
|
|
||||||
set(path "@executable_path/../MacOS")
|
|
||||||
set(overridden 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS):
|
|
||||||
#
|
|
||||||
if(NOT overridden)
|
|
||||||
if(item MATCHES "[^/]+\\.framework/")
|
|
||||||
set(path "@executable_path/../Frameworks")
|
|
||||||
set(overridden 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Provide a hook so that projects can override the default embedded location
|
|
||||||
# of any given library by whatever logic they choose:
|
|
||||||
#
|
|
||||||
if(COMMAND gp_item_default_embedded_path_override)
|
|
||||||
gp_item_default_embedded_path_override("${item}" path)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${default_embedded_path_var} "${path}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(gp_resolve_item context item exepath dirs resolved_item_var)
|
|
||||||
set(resolved 0)
|
|
||||||
set(resolved_item "${item}")
|
|
||||||
|
|
||||||
# Is it already resolved?
|
|
||||||
#
|
|
||||||
if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}")
|
|
||||||
set(resolved 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT resolved)
|
|
||||||
if(item MATCHES "@executable_path")
|
|
||||||
#
|
|
||||||
# @executable_path references are assumed relative to exepath
|
|
||||||
#
|
|
||||||
string(REPLACE "@executable_path" "${exepath}" ri "${item}")
|
|
||||||
get_filename_component(ri "${ri}" ABSOLUTE)
|
|
||||||
|
|
||||||
if(EXISTS "${ri}")
|
|
||||||
#message(STATUS "info: embedded item exists (${ri})")
|
|
||||||
set(resolved 1)
|
|
||||||
set(resolved_item "${ri}")
|
|
||||||
else()
|
|
||||||
message(STATUS "warning: embedded item does not exist '${ri}'")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT resolved)
|
|
||||||
if(item MATCHES "@loader_path")
|
|
||||||
#
|
|
||||||
# @loader_path references are assumed relative to the
|
|
||||||
# PATH of the given "context" (presumably another library)
|
|
||||||
#
|
|
||||||
get_filename_component(contextpath "${context}" PATH)
|
|
||||||
string(REPLACE "@loader_path" "${contextpath}" ri "${item}")
|
|
||||||
get_filename_component(ri "${ri}" ABSOLUTE)
|
|
||||||
|
|
||||||
if(EXISTS "${ri}")
|
|
||||||
#message(STATUS "info: embedded item exists (${ri})")
|
|
||||||
set(resolved 1)
|
|
||||||
set(resolved_item "${ri}")
|
|
||||||
else()
|
|
||||||
message(STATUS "warning: embedded item does not exist '${ri}'")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT resolved)
|
|
||||||
if(item MATCHES "@rpath")
|
|
||||||
#
|
|
||||||
# @rpath references are relative to the paths built into the binaries with -rpath
|
|
||||||
# We handle this case like we do for other Unixes
|
|
||||||
#
|
|
||||||
string(REPLACE "@rpath/" "" norpath_item "${item}")
|
|
||||||
|
|
||||||
set(ri "ri-NOTFOUND")
|
|
||||||
find_file(ri "${norpath_item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
|
|
||||||
if(ri)
|
|
||||||
#message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
|
|
||||||
set(resolved 1)
|
|
||||||
set(resolved_item "${ri}")
|
|
||||||
set(ri "ri-NOTFOUND")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT resolved)
|
|
||||||
set(ri "ri-NOTFOUND")
|
|
||||||
find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
|
|
||||||
find_file(ri "${item}" ${exepath} ${dirs} /usr/lib)
|
|
||||||
if(ri)
|
|
||||||
#message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
|
|
||||||
set(resolved 1)
|
|
||||||
set(resolved_item "${ri}")
|
|
||||||
set(ri "ri-NOTFOUND")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT resolved)
|
|
||||||
if(item MATCHES "[^/]+\\.framework/")
|
|
||||||
set(fw "fw-NOTFOUND")
|
|
||||||
find_file(fw "${item}"
|
|
||||||
"~/Library/Frameworks"
|
|
||||||
"/Library/Frameworks"
|
|
||||||
"/System/Library/Frameworks"
|
|
||||||
)
|
|
||||||
if(fw)
|
|
||||||
#message(STATUS "info: 'find_file' found framework (${fw})")
|
|
||||||
set(resolved 1)
|
|
||||||
set(resolved_item "${fw}")
|
|
||||||
set(fw "fw-NOTFOUND")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Using find_program on Windows will find dll files that are in the PATH.
|
|
||||||
# (Converting simple file names into full path names if found.)
|
|
||||||
#
|
|
||||||
if(WIN32 AND NOT UNIX)
|
|
||||||
if(NOT resolved)
|
|
||||||
set(ri "ri-NOTFOUND")
|
|
||||||
find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH)
|
|
||||||
find_program(ri "${item}" PATHS "${exepath};${dirs}")
|
|
||||||
if(ri)
|
|
||||||
#message(STATUS "info: 'find_program' in exepath/dirs (${ri})")
|
|
||||||
set(resolved 1)
|
|
||||||
set(resolved_item "${ri}")
|
|
||||||
set(ri "ri-NOTFOUND")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Provide a hook so that projects can override item resolution
|
|
||||||
# by whatever logic they choose:
|
|
||||||
#
|
|
||||||
if(COMMAND gp_resolve_item_override)
|
|
||||||
gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT resolved)
|
|
||||||
message(STATUS "
|
|
||||||
warning: cannot resolve item '${item}'
|
|
||||||
|
|
||||||
possible problems:
|
|
||||||
need more directories?
|
|
||||||
need to use InstallRequiredSystemLibraries?
|
|
||||||
run in install tree instead of build tree?
|
|
||||||
")
|
|
||||||
# message(STATUS "
|
|
||||||
#******************************************************************************
|
|
||||||
#warning: cannot resolve item '${item}'
|
|
||||||
#
|
|
||||||
# possible problems:
|
|
||||||
# need more directories?
|
|
||||||
# need to use InstallRequiredSystemLibraries?
|
|
||||||
# run in install tree instead of build tree?
|
|
||||||
#
|
|
||||||
# context='${context}'
|
|
||||||
# item='${item}'
|
|
||||||
# exepath='${exepath}'
|
|
||||||
# dirs='${dirs}'
|
|
||||||
# resolved_item_var='${resolved_item_var}'
|
|
||||||
#******************************************************************************
|
|
||||||
#")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(gp_resolved_file_type original_file file exepath dirs type_var)
|
|
||||||
#message(STATUS "**")
|
|
||||||
|
|
||||||
if(NOT IS_ABSOLUTE "${original_file}")
|
|
||||||
message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(is_embedded 0)
|
|
||||||
set(is_local 0)
|
|
||||||
set(is_system 0)
|
|
||||||
|
|
||||||
set(resolved_file "${file}")
|
|
||||||
|
|
||||||
if("${file}" MATCHES "^@(executable|loader)_path")
|
|
||||||
set(is_embedded 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT is_embedded)
|
|
||||||
if(NOT IS_ABSOLUTE "${file}")
|
|
||||||
gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
string(TOLOWER "${original_file}" original_lower)
|
|
||||||
string(TOLOWER "${resolved_file}" lower)
|
|
||||||
|
|
||||||
if(UNIX)
|
|
||||||
if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)")
|
|
||||||
set(is_system 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)")
|
|
||||||
set(is_system 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
string(TOLOWER "$ENV{SystemRoot}" sysroot)
|
|
||||||
string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}")
|
|
||||||
|
|
||||||
string(TOLOWER "$ENV{windir}" windir)
|
|
||||||
string(REGEX REPLACE "\\\\" "/" windir "${windir}")
|
|
||||||
|
|
||||||
if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)")
|
|
||||||
set(is_system 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(UNIX)
|
|
||||||
# if cygwin, we can get the properly formed windows paths from cygpath
|
|
||||||
find_program(CYGPATH_EXECUTABLE cygpath)
|
|
||||||
|
|
||||||
if(CYGPATH_EXECUTABLE)
|
|
||||||
execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W
|
|
||||||
OUTPUT_VARIABLE env_windir
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S
|
|
||||||
OUTPUT_VARIABLE env_sysdir
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
||||||
string(TOLOWER "${env_windir}" windir)
|
|
||||||
string(TOLOWER "${env_sysdir}" sysroot)
|
|
||||||
|
|
||||||
if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)")
|
|
||||||
set(is_system 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT is_system)
|
|
||||||
get_filename_component(original_path "${original_lower}" PATH)
|
|
||||||
get_filename_component(path "${lower}" PATH)
|
|
||||||
if("${original_path}" STREQUAL "${path}")
|
|
||||||
set(is_local 1)
|
|
||||||
else()
|
|
||||||
string(LENGTH "${original_path}/" original_length)
|
|
||||||
string(LENGTH "${lower}" path_length)
|
|
||||||
if(${path_length} GREATER ${original_length})
|
|
||||||
string(SUBSTRING "${lower}" 0 ${original_length} path)
|
|
||||||
if("${original_path}/" STREQUAL "${path}")
|
|
||||||
set(is_embedded 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Return type string based on computed booleans:
|
|
||||||
#
|
|
||||||
set(type "other")
|
|
||||||
|
|
||||||
if(is_system)
|
|
||||||
set(type "system")
|
|
||||||
elseif(is_embedded)
|
|
||||||
set(type "embedded")
|
|
||||||
elseif(is_local)
|
|
||||||
set(type "local")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
#message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'")
|
|
||||||
#message(STATUS " type: '${type}'")
|
|
||||||
|
|
||||||
if(NOT is_embedded)
|
|
||||||
if(NOT IS_ABSOLUTE "${resolved_file}")
|
|
||||||
if(lower MATCHES "^msvc[^/]+dll" AND is_system)
|
|
||||||
message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'")
|
|
||||||
else()
|
|
||||||
message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Provide a hook so that projects can override the decision on whether a
|
|
||||||
# library belongs to the system or not by whatever logic they choose:
|
|
||||||
#
|
|
||||||
if(COMMAND gp_resolved_file_type_override)
|
|
||||||
gp_resolved_file_type_override("${resolved_file}" type)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${type_var} "${type}" PARENT_SCOPE)
|
|
||||||
|
|
||||||
#message(STATUS "**")
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(gp_file_type original_file file type_var)
|
|
||||||
if(NOT IS_ABSOLUTE "${original_file}")
|
|
||||||
message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
get_filename_component(exepath "${original_file}" PATH)
|
|
||||||
|
|
||||||
set(type "")
|
|
||||||
gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type)
|
|
||||||
|
|
||||||
set(${type_var} "${type}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs)
|
|
||||||
set(verbose 0)
|
|
||||||
set(eol_char "E")
|
|
||||||
|
|
||||||
if(NOT IS_ABSOLUTE "${target}")
|
|
||||||
message("warning: target '${target}' is not absolute...")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT EXISTS "${target}")
|
|
||||||
message("warning: target '${target}' does not exist...")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(gp_cmd_paths ${gp_cmd_paths}
|
|
||||||
"C:/Program Files/Microsoft Visual Studio 9.0/VC/bin"
|
|
||||||
"C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin"
|
|
||||||
"C:/Program Files/Microsoft Visual Studio 8/VC/BIN"
|
|
||||||
"C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN"
|
|
||||||
"C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN"
|
|
||||||
"C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN"
|
|
||||||
"/usr/local/bin"
|
|
||||||
"/usr/bin"
|
|
||||||
)
|
|
||||||
|
|
||||||
# <setup-gp_tool-vars>
|
|
||||||
#
|
|
||||||
# Try to choose the right tool by default. Caller can set gp_tool prior to
|
|
||||||
# calling this function to force using a different tool.
|
|
||||||
#
|
|
||||||
if("${gp_tool}" STREQUAL "")
|
|
||||||
set(gp_tool "ldd")
|
|
||||||
|
|
||||||
if(APPLE)
|
|
||||||
set(gp_tool "otool")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har!
|
|
||||||
find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths})
|
|
||||||
if(gp_dumpbin)
|
|
||||||
set(gp_tool "dumpbin")
|
|
||||||
else() # Try harder. Maybe we're on MinGW
|
|
||||||
set(gp_tool "objdump")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths})
|
|
||||||
|
|
||||||
if(NOT gp_cmd)
|
|
||||||
message(FATAL_ERROR "FATAL ERROR: could not find '${gp_tool}' - cannot analyze prerequisites!")
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(gp_tool_known 0)
|
|
||||||
|
|
||||||
if("${gp_tool}" STREQUAL "ldd")
|
|
||||||
set(gp_cmd_args "")
|
|
||||||
set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$")
|
|
||||||
set(gp_regex_error "not found${eol_char}$")
|
|
||||||
set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$")
|
|
||||||
set(gp_regex_cmp_count 1)
|
|
||||||
set(gp_tool_known 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${gp_tool}" STREQUAL "otool")
|
|
||||||
set(gp_cmd_args "-L")
|
|
||||||
set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$")
|
|
||||||
set(gp_regex_error "")
|
|
||||||
set(gp_regex_fallback "")
|
|
||||||
set(gp_regex_cmp_count 3)
|
|
||||||
set(gp_tool_known 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${gp_tool}" STREQUAL "dumpbin")
|
|
||||||
set(gp_cmd_args "/dependents")
|
|
||||||
set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$")
|
|
||||||
set(gp_regex_error "")
|
|
||||||
set(gp_regex_fallback "")
|
|
||||||
set(gp_regex_cmp_count 1)
|
|
||||||
set(gp_tool_known 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${gp_tool}" STREQUAL "objdump")
|
|
||||||
set(gp_cmd_args "-p")
|
|
||||||
set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$")
|
|
||||||
set(gp_regex_error "")
|
|
||||||
set(gp_regex_fallback "")
|
|
||||||
set(gp_regex_cmp_count 1)
|
|
||||||
set(gp_tool_known 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT gp_tool_known)
|
|
||||||
message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...")
|
|
||||||
message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'")
|
|
||||||
message(STATUS "Valid gp_tool values are dumpbin, ldd, objdump and otool.")
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
if("${gp_tool}" STREQUAL "dumpbin")
|
|
||||||
# When running dumpbin, it also needs the "Common7/IDE" directory in the
|
|
||||||
# PATH. It will already be in the PATH if being run from a Visual Studio
|
|
||||||
# command prompt. Add it to the PATH here in case we are running from a
|
|
||||||
# different command prompt.
|
|
||||||
#
|
|
||||||
get_filename_component(gp_cmd_dir "${gp_cmd}" PATH)
|
|
||||||
get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE)
|
|
||||||
# Use cmake paths as a user may have a PATH element ending with a backslash.
|
|
||||||
# This will escape the list delimiter and create havoc!
|
|
||||||
if(EXISTS "${gp_cmd_dlls_dir}")
|
|
||||||
# only add to the path if it is not already in the path
|
|
||||||
set(gp_found_cmd_dlls_dir 0)
|
|
||||||
file(TO_CMAKE_PATH "$ENV{PATH}" env_path)
|
|
||||||
foreach(gp_env_path_element ${env_path})
|
|
||||||
if("${gp_env_path_element}" STREQUAL "${gp_cmd_dlls_dir}")
|
|
||||||
set(gp_found_cmd_dlls_dir 1)
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(NOT gp_found_cmd_dlls_dir)
|
|
||||||
file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir)
|
|
||||||
set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
#
|
|
||||||
# </setup-gp_tool-vars>
|
|
||||||
|
|
||||||
if("${gp_tool}" STREQUAL "ldd")
|
|
||||||
set(old_ld_env "$ENV{LD_LIBRARY_PATH}")
|
|
||||||
foreach(dir ${exepath} ${dirs})
|
|
||||||
set(ENV{LD_LIBRARY_PATH} "${dir}:$ENV{LD_LIBRARY_PATH}")
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# Track new prerequisites at each new level of recursion. Start with an
|
|
||||||
# empty list at each level:
|
|
||||||
#
|
|
||||||
set(unseen_prereqs)
|
|
||||||
|
|
||||||
# Run gp_cmd on the target:
|
|
||||||
#
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${gp_cmd} ${gp_cmd_args} ${target}
|
|
||||||
OUTPUT_VARIABLE gp_cmd_ov
|
|
||||||
)
|
|
||||||
|
|
||||||
if("${gp_tool}" STREQUAL "ldd")
|
|
||||||
set(ENV{LD_LIBRARY_PATH} "${old_ld_env}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(verbose)
|
|
||||||
message(STATUS "<RawOutput cmd='${gp_cmd} ${gp_cmd_args} ${target}'>")
|
|
||||||
message(STATUS "gp_cmd_ov='${gp_cmd_ov}'")
|
|
||||||
message(STATUS "</RawOutput>")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
get_filename_component(target_dir "${target}" PATH)
|
|
||||||
|
|
||||||
# Convert to a list of lines:
|
|
||||||
#
|
|
||||||
string(REGEX REPLACE ";" "\\\\;" candidates "${gp_cmd_ov}")
|
|
||||||
string(REGEX REPLACE "\n" "${eol_char};" candidates "${candidates}")
|
|
||||||
|
|
||||||
# check for install id and remove it from list, since otool -L can include a
|
|
||||||
# reference to itself
|
|
||||||
set(gp_install_id)
|
|
||||||
if("${gp_tool}" STREQUAL "otool")
|
|
||||||
execute_process(
|
|
||||||
COMMAND otool -D ${target}
|
|
||||||
OUTPUT_VARIABLE gp_install_id_ov
|
|
||||||
)
|
|
||||||
# second line is install name
|
|
||||||
string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}")
|
|
||||||
if(gp_install_id)
|
|
||||||
# trim
|
|
||||||
string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}")
|
|
||||||
#message("INSTALL ID is \"${gp_install_id}\"")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Analyze each line for file names that match the regular expression:
|
|
||||||
#
|
|
||||||
foreach(candidate ${candidates})
|
|
||||||
if("${candidate}" MATCHES "${gp_regex}")
|
|
||||||
|
|
||||||
# Extract information from each candidate:
|
|
||||||
if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}")
|
|
||||||
string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}")
|
|
||||||
else()
|
|
||||||
string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(gp_regex_cmp_count GREATER 1)
|
|
||||||
string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}")
|
|
||||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}")
|
|
||||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}")
|
|
||||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(gp_regex_cmp_count GREATER 2)
|
|
||||||
string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}")
|
|
||||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}")
|
|
||||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}")
|
|
||||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Use the raw_item as the list entries returned by this function. Use the
|
|
||||||
# gp_resolve_item function to resolve it to an actual full path file if
|
|
||||||
# necessary.
|
|
||||||
#
|
|
||||||
set(item "${raw_item}")
|
|
||||||
|
|
||||||
# Add each item unless it is excluded:
|
|
||||||
#
|
|
||||||
set(add_item 1)
|
|
||||||
|
|
||||||
if("${item}" STREQUAL "${gp_install_id}")
|
|
||||||
set(add_item 0)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(add_item AND ${exclude_system})
|
|
||||||
set(type "")
|
|
||||||
gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type)
|
|
||||||
|
|
||||||
if("${type}" STREQUAL "system")
|
|
||||||
set(add_item 0)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(add_item)
|
|
||||||
list(LENGTH ${prerequisites_var} list_length_before_append)
|
|
||||||
gp_append_unique(${prerequisites_var} "${item}")
|
|
||||||
list(LENGTH ${prerequisites_var} list_length_after_append)
|
|
||||||
|
|
||||||
if(${recurse})
|
|
||||||
# If item was really added, this is the first time we have seen it.
|
|
||||||
# Add it to unseen_prereqs so that we can recursively add *its*
|
|
||||||
# prerequisites...
|
|
||||||
#
|
|
||||||
# But first: resolve its name to an absolute full path name such
|
|
||||||
# that the analysis tools can simply accept it as input.
|
|
||||||
#
|
|
||||||
if(NOT list_length_before_append EQUAL list_length_after_append)
|
|
||||||
gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item)
|
|
||||||
set(unseen_prereqs ${unseen_prereqs} "${resolved_item}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(verbose)
|
|
||||||
message(STATUS "ignoring non-matching line: '${candidate}'")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
list(LENGTH ${prerequisites_var} prerequisites_var_length)
|
|
||||||
if(prerequisites_var_length GREATER 0)
|
|
||||||
list(SORT ${prerequisites_var})
|
|
||||||
endif()
|
|
||||||
if(${recurse})
|
|
||||||
set(more_inputs ${unseen_prereqs})
|
|
||||||
foreach(input ${more_inputs})
|
|
||||||
get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}")
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(list_prerequisites target)
|
|
||||||
if("${ARGV1}" STREQUAL "")
|
|
||||||
set(all 1)
|
|
||||||
else()
|
|
||||||
set(all "${ARGV1}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${ARGV2}" STREQUAL "")
|
|
||||||
set(exclude_system 0)
|
|
||||||
else()
|
|
||||||
set(exclude_system "${ARGV2}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if("${ARGV3}" STREQUAL "")
|
|
||||||
set(verbose 0)
|
|
||||||
else()
|
|
||||||
set(verbose "${ARGV3}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(count 0)
|
|
||||||
set(count_str "")
|
|
||||||
set(print_count "${verbose}")
|
|
||||||
set(print_prerequisite_type "${verbose}")
|
|
||||||
set(print_target "${verbose}")
|
|
||||||
set(type_str "")
|
|
||||||
|
|
||||||
get_filename_component(exepath "${target}" PATH)
|
|
||||||
|
|
||||||
set(prereqs "")
|
|
||||||
get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "")
|
|
||||||
|
|
||||||
if(print_target)
|
|
||||||
message(STATUS "File '${target}' depends on:")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
foreach(d ${prereqs})
|
|
||||||
math(EXPR count "${count} + 1")
|
|
||||||
|
|
||||||
if(print_count)
|
|
||||||
set(count_str "${count}. ")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(print_prerequisite_type)
|
|
||||||
gp_file_type("${target}" "${d}" type)
|
|
||||||
set(type_str " (${type})")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
message(STATUS "${count_str}${d}${type_str}")
|
|
||||||
endforeach()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
function(list_prerequisites_by_glob glob_arg glob_exp)
|
|
||||||
message(STATUS "=============================================================================")
|
|
||||||
message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'")
|
|
||||||
message(STATUS "")
|
|
||||||
file(${glob_arg} file_list ${glob_exp})
|
|
||||||
foreach(f ${file_list})
|
|
||||||
is_file_executable("${f}" is_f_executable)
|
|
||||||
if(is_f_executable)
|
|
||||||
message(STATUS "=============================================================================")
|
|
||||||
list_prerequisites("${f}" ${ARGN})
|
|
||||||
message(STATUS "")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endfunction()
|
|
@ -2,6 +2,10 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>A Minecraft mod wants to access your camera.</string>
|
||||||
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
|
<string>A Minecraft mod wants to access your microphone.</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>NSHighResolutionCapable</key>
|
<key>NSHighResolutionCapable</key>
|
||||||
@ -36,5 +40,32 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||||
|
<key>SUPublicEDKey</key>
|
||||||
|
<string>${MACOSX_SPARKLE_UPDATE_PUBLIC_KEY}</string>
|
||||||
|
<key>SUFeedURL</key>
|
||||||
|
<string>${MACOSX_SPARKLE_UPDATE_FEED_URL}</string>
|
||||||
|
<key>CFBundleDocumentTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeExtensions</key>
|
||||||
|
<array>
|
||||||
|
<string>zip</string>
|
||||||
|
<string>mrpack</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>Prism Launcher instance</string>
|
||||||
|
<key>CFBundleTypeOSTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>TEXT</string>
|
||||||
|
<string>utxt</string>
|
||||||
|
<string>TUTX</string>
|
||||||
|
<string>****</string>
|
||||||
|
</array>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Alternate</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
if(__QMAKEQUERY_CMAKE__)
|
|
||||||
return()
|
|
||||||
endif()
|
|
||||||
set(__QMAKEQUERY_CMAKE__ TRUE)
|
|
||||||
|
|
||||||
get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
|
|
||||||
|
|
||||||
function(QUERY_QMAKE VAR RESULT)
|
|
||||||
exec_program(${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output )
|
|
||||||
if(NOT return_code)
|
|
||||||
file(TO_CMAKE_PATH "${output}" output)
|
|
||||||
set(${RESULT} ${output} PARENT_SCOPE)
|
|
||||||
endif(NOT return_code)
|
|
||||||
endfunction(QUERY_QMAKE)
|
|
38
cmake/QtVersionOption.cmake
Normal file
38
cmake/QtVersionOption.cmake
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#.rst:
|
||||||
|
# QtVersionOption
|
||||||
|
# ---------------
|
||||||
|
#
|
||||||
|
# Adds a build option to select the major Qt version if necessary,
|
||||||
|
# that is, if the major Qt version has not yet been determined otherwise
|
||||||
|
# (e.g. by a corresponding find_package() call).
|
||||||
|
#
|
||||||
|
# This module is typically included by other modules requiring knowledge
|
||||||
|
# about the major Qt version.
|
||||||
|
#
|
||||||
|
# ``QT_MAJOR_VERSION`` is defined to either be "5" or "6".
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Since 5.82.0.
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
# SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
if (DEFINED QT_MAJOR_VERSION)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (TARGET Qt5::Core)
|
||||||
|
set(QT_MAJOR_VERSION 5)
|
||||||
|
elseif (TARGET Qt6::Core)
|
||||||
|
set(QT_MAJOR_VERSION 6)
|
||||||
|
else()
|
||||||
|
option(BUILD_WITH_QT6 "Build against Qt 6" OFF)
|
||||||
|
|
||||||
|
if (BUILD_WITH_QT6)
|
||||||
|
set(QT_MAJOR_VERSION 6)
|
||||||
|
else()
|
||||||
|
set(QT_MAJOR_VERSION 5)
|
||||||
|
endif()
|
||||||
|
endif()
|
97
cmake/QtVersionlessBackport.cmake
Normal file
97
cmake/QtVersionlessBackport.cmake
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#=============================================================================
|
||||||
|
# Copyright 2005-2011 Kitware, Inc.
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# * 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.
|
||||||
|
#
|
||||||
|
# * Neither the name of Kitware, Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived
|
||||||
|
# from this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||||
|
# HOLDER 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.
|
||||||
|
#=============================================================================
|
||||||
|
|
||||||
|
# From Qt5CoreMacros.cmake
|
||||||
|
|
||||||
|
function(qt_generate_moc)
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
qt5_generate_moc(${ARGV})
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
qt6_generate_moc(${ARGV})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(qt_wrap_cpp outfiles)
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
qt5_wrap_cpp("${outfiles}" ${ARGN})
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
qt6_wrap_cpp("${outfiles}" ${ARGN})
|
||||||
|
endif()
|
||||||
|
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(qt_add_binary_resources)
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
qt5_add_binary_resources(${ARGV})
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
qt6_add_binary_resources(${ARGV})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(qt_add_resources outfiles)
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
qt5_add_resources("${outfiles}" ${ARGN})
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
qt6_add_resources("${outfiles}" ${ARGN})
|
||||||
|
endif()
|
||||||
|
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(qt_add_big_resources outfiles)
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
qt5_add_big_resources(${outfiles} ${ARGN})
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
qt6_add_big_resources(${outfiles} ${ARGN})
|
||||||
|
endif()
|
||||||
|
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(qt_import_plugins)
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
qt5_import_plugins(${ARGV})
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
qt6_import_plugins(${ARGV})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
# From Qt5WidgetsMacros.cmake
|
||||||
|
|
||||||
|
function(qt_wrap_ui outfiles)
|
||||||
|
if(QT_VERSION_MAJOR EQUAL 5)
|
||||||
|
qt5_wrap_ui("${outfiles}" ${ARGN})
|
||||||
|
elseif(QT_VERSION_MAJOR EQUAL 6)
|
||||||
|
qt6_wrap_ui("${outfiles}" ${ARGN})
|
||||||
|
endif()
|
||||||
|
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
find_package(Qt5Test REQUIRED)
|
|
||||||
|
|
||||||
set(TEST_RESOURCE_PATH ${CMAKE_CURRENT_LIST_DIR})
|
|
||||||
|
|
||||||
message(${TEST_RESOURCE_PATH})
|
|
||||||
|
|
||||||
function(add_unit_test name)
|
|
||||||
set(options "")
|
|
||||||
set(oneValueArgs DATA)
|
|
||||||
set(multiValueArgs SOURCES LIBS)
|
|
||||||
|
|
||||||
cmake_parse_arguments(OPT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
add_executable(${name}_test ${OPT_SOURCES} ${TEST_RESOURCE_PATH}/UnitTest/test.rc)
|
|
||||||
else()
|
|
||||||
add_executable(${name}_test ${OPT_SOURCES})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT "${OPT_DATA}" STREQUAL "")
|
|
||||||
set(TEST_DATA_PATH "${CMAKE_CURRENT_BINARY_DIR}/data")
|
|
||||||
set(TEST_DATA_PATH_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${OPT_DATA}")
|
|
||||||
message("From ${TEST_DATA_PATH_SRC} to ${TEST_DATA_PATH}")
|
|
||||||
string(REGEX REPLACE "[/\\:]" "_" DATA_TARGET_NAME "${TEST_DATA_PATH_SRC}")
|
|
||||||
if(UNIX)
|
|
||||||
# on unix we get the third / from the filename
|
|
||||||
set(TEST_DATA_URL "file://${TEST_DATA_PATH}")
|
|
||||||
else()
|
|
||||||
# we don't on windows, so we have to add it ourselves
|
|
||||||
set(TEST_DATA_URL "file:///${TEST_DATA_PATH}")
|
|
||||||
endif()
|
|
||||||
if(NOT TARGET "${DATA_TARGET_NAME}")
|
|
||||||
add_custom_target(${DATA_TARGET_NAME})
|
|
||||||
add_dependencies(${name}_test ${DATA_TARGET_NAME})
|
|
||||||
add_custom_command(
|
|
||||||
TARGET ${DATA_TARGET_NAME}
|
|
||||||
COMMAND ${CMAKE_COMMAND} "-DTEST_DATA_URL=${TEST_DATA_URL}" -DSOURCE=${TEST_DATA_PATH_SRC} -DDESTINATION=${TEST_DATA_PATH} -P ${TEST_RESOURCE_PATH}/UnitTest/generate_test_data.cmake
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(${name}_test Qt5::Test ${OPT_LIBS})
|
|
||||||
|
|
||||||
target_include_directories(${name}_test PRIVATE "${TEST_RESOURCE_PATH}/UnitTest/")
|
|
||||||
|
|
||||||
add_test(NAME ${name} COMMAND ${name}_test WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
endfunction()
|
|
@ -1,28 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QFile>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QTest>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
#define expandstr(s) expandstr2(s)
|
|
||||||
#define expandstr2(s) #s
|
|
||||||
|
|
||||||
class TestsInternal
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static QByteArray readFile(const QString &fileName)
|
|
||||||
{
|
|
||||||
QFile f(fileName);
|
|
||||||
f.open(QFile::ReadOnly);
|
|
||||||
return f.readAll();
|
|
||||||
}
|
|
||||||
static QString readFileUtf8(const QString &fileName)
|
|
||||||
{
|
|
||||||
return QString::fromUtf8(readFile(fileName));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define GET_TEST_FILE(file) TestsInternal::readFile(QFINDTESTDATA(file))
|
|
||||||
#define GET_TEST_FILE_UTF8(file) TestsInternal::readFileUtf8(QFINDTESTDATA(file))
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
# Copy files from source directory to destination directory, substituting any
|
|
||||||
# variables. Create destination directory if it does not exist.
|
|
||||||
|
|
||||||
function(configure_files srcDir destDir)
|
|
||||||
make_directory(${destDir})
|
|
||||||
|
|
||||||
file(GLOB templateFiles RELATIVE ${srcDir} ${srcDir}/*)
|
|
||||||
foreach(templateFile ${templateFiles})
|
|
||||||
set(srcTemplatePath ${srcDir}/${templateFile})
|
|
||||||
if(NOT IS_DIRECTORY ${srcTemplatePath})
|
|
||||||
configure_file(
|
|
||||||
${srcTemplatePath}
|
|
||||||
${destDir}/${templateFile}
|
|
||||||
@ONLY
|
|
||||||
NEWLINE_STYLE LF
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
configure_files("${srcTemplatePath}" "${destDir}/${templateFile}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
configure_files(${SOURCE} ${DESTINATION})
|
|
@ -1,27 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<assemblyIdentity name="Launcher.Test.0" type="win32" version="5.0.0.0" />
|
|
||||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
|
||||||
<security>
|
|
||||||
<requestedPrivileges>
|
|
||||||
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
|
|
||||||
</requestedPrivileges>
|
|
||||||
</security>
|
|
||||||
</trustInfo>
|
|
||||||
<dependency>
|
|
||||||
<dependentAssembly>
|
|
||||||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
|
|
||||||
</dependentAssembly>
|
|
||||||
</dependency>
|
|
||||||
<description>Custom Minecraft launcher for managing multiple installs.</description>
|
|
||||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
|
||||||
<application>
|
|
||||||
<!--The ID below indicates app support for Windows Vista -->
|
|
||||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
|
||||||
<!--The ID below indicates app support for Windows 7 -->
|
|
||||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
|
||||||
<!--The ID below indicates app support for Windows Developer Preview / Windows 8 -->
|
|
||||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
|
||||||
</application>
|
|
||||||
</compatibility>
|
|
||||||
</assembly>
|
|
@ -1,28 +0,0 @@
|
|||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
1 RT_MANIFEST "test.manifest"
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
|
||||||
FILEVERSION 1,0,0,0
|
|
||||||
FILEOS VOS_NT_WINDOWS32
|
|
||||||
FILETYPE VFT_APP
|
|
||||||
BEGIN
|
|
||||||
BLOCK "StringFileInfo"
|
|
||||||
BEGIN
|
|
||||||
BLOCK "000004b0"
|
|
||||||
BEGIN
|
|
||||||
VALUE "CompanyName", "MultiMC & PolyMC Contributors"
|
|
||||||
VALUE "FileDescription", "Testcase"
|
|
||||||
VALUE "FileVersion", "1.0.0.0"
|
|
||||||
VALUE "ProductName", "Launcher Testcase"
|
|
||||||
VALUE "ProductVersion", "5"
|
|
||||||
END
|
|
||||||
END
|
|
||||||
BLOCK "VarFileInfo"
|
|
||||||
BEGIN
|
|
||||||
VALUE "Translation", 0x0000, 0x04b0 // Unicode
|
|
||||||
END
|
|
||||||
END
|
|
@ -1,881 +0,0 @@
|
|||||||
# - Use Module for Java
|
|
||||||
# This file provides functions for Java. It is assumed that FindJava.cmake
|
|
||||||
# has already been loaded. See FindJava.cmake for information on how to
|
|
||||||
# load Java into your CMake project.
|
|
||||||
#
|
|
||||||
# add_jar(TARGET_NAME SRC1 SRC2 .. SRCN RCS1 RCS2 .. RCSN)
|
|
||||||
#
|
|
||||||
# This command creates a <TARGET_NAME>.jar. It compiles the given source
|
|
||||||
# files (SRC) and adds the given resource files (RCS) to the jar file.
|
|
||||||
# If only resource files are given then just a jar file is created.
|
|
||||||
#
|
|
||||||
# Additional instructions:
|
|
||||||
# To add compile flags to the target you can set these flags with
|
|
||||||
# the following variable:
|
|
||||||
#
|
|
||||||
# set(CMAKE_JAVA_COMPILE_FLAGS -nowarn)
|
|
||||||
#
|
|
||||||
# To add a path or a jar file to the class path you can do this
|
|
||||||
# with the CMAKE_JAVA_INCLUDE_PATH variable.
|
|
||||||
#
|
|
||||||
# set(CMAKE_JAVA_INCLUDE_PATH /usr/share/java/shibboleet.jar)
|
|
||||||
#
|
|
||||||
# To use a different output name for the target you can set it with:
|
|
||||||
#
|
|
||||||
# set(CMAKE_JAVA_TARGET_OUTPUT_NAME shibboleet.jar)
|
|
||||||
# add_jar(foobar foobar.java)
|
|
||||||
#
|
|
||||||
# To use a different output directory than CMAKE_CURRENT_BINARY_DIR
|
|
||||||
# you can set it with:
|
|
||||||
#
|
|
||||||
# set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/bin)
|
|
||||||
#
|
|
||||||
# To define an entry point in your jar you can set it with:
|
|
||||||
#
|
|
||||||
# set(CMAKE_JAVA_JAR_ENTRY_POINT com/examples/MyProject/Main)
|
|
||||||
#
|
|
||||||
# To add a VERSION to the target output name you can set it using
|
|
||||||
# CMAKE_JAVA_TARGET_VERSION. This will create a jar file with the name
|
|
||||||
# shibboleet-1.0.0.jar and will create a symlink shibboleet.jar
|
|
||||||
# pointing to the jar with the version information.
|
|
||||||
#
|
|
||||||
# set(CMAKE_JAVA_TARGET_VERSION 1.2.0)
|
|
||||||
# add_jar(shibboleet shibbotleet.java)
|
|
||||||
#
|
|
||||||
# If the target is a JNI library, utilize the following commands to
|
|
||||||
# create a JNI symbolic link:
|
|
||||||
#
|
|
||||||
# set(CMAKE_JNI_TARGET TRUE)
|
|
||||||
# set(CMAKE_JAVA_TARGET_VERSION 1.2.0)
|
|
||||||
# add_jar(shibboleet shibbotleet.java)
|
|
||||||
# install_jar(shibboleet ${LIB_INSTALL_DIR}/shibboleet)
|
|
||||||
# install_jni_symlink(shibboleet ${JAVA_LIB_INSTALL_DIR})
|
|
||||||
#
|
|
||||||
# If a single target needs to produce more than one jar from its
|
|
||||||
# java source code, to prevent the accumulation of duplicate class
|
|
||||||
# files in subsequent jars, set/reset CMAKE_JAR_CLASSES_PREFIX prior
|
|
||||||
# to calling the add_jar() function:
|
|
||||||
#
|
|
||||||
# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/foo)
|
|
||||||
# add_jar(foo foo.java)
|
|
||||||
#
|
|
||||||
# set(CMAKE_JAR_CLASSES_PREFIX com/redhat/bar)
|
|
||||||
# add_jar(bar bar.java)
|
|
||||||
#
|
|
||||||
# Target Properties:
|
|
||||||
# The add_jar() functions sets some target properties. You can get these
|
|
||||||
# properties with the
|
|
||||||
# get_property(TARGET <target_name> PROPERTY <propery_name>)
|
|
||||||
# command.
|
|
||||||
#
|
|
||||||
# INSTALL_FILES The files which should be installed. This is used by
|
|
||||||
# install_jar().
|
|
||||||
# JNI_SYMLINK The JNI symlink which should be installed.
|
|
||||||
# This is used by install_jni_symlink().
|
|
||||||
# JAR_FILE The location of the jar file so that you can include
|
|
||||||
# it.
|
|
||||||
# CLASS_DIR The directory where the class files can be found. For
|
|
||||||
# example to use them with javah.
|
|
||||||
#
|
|
||||||
# find_jar(<VAR>
|
|
||||||
# name | NAMES name1 [name2 ...]
|
|
||||||
# [PATHS path1 [path2 ... ENV var]]
|
|
||||||
# [VERSIONS version1 [version2]]
|
|
||||||
# [DOC "cache documentation string"]
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# This command is used to find a full path to the named jar. A cache
|
|
||||||
# entry named by <VAR> is created to stor the result of this command. If
|
|
||||||
# the full path to a jar is found the result is stored in the variable
|
|
||||||
# and the search will not repeated unless the variable is cleared. If
|
|
||||||
# nothing is found, the result will be <VAR>-NOTFOUND, and the search
|
|
||||||
# will be attempted again next time find_jar is invoked with the same
|
|
||||||
# variable.
|
|
||||||
# The name of the full path to a file that is searched for is specified
|
|
||||||
# by the names listed after NAMES argument. Additional search locations
|
|
||||||
# can be specified after the PATHS argument. If you require special a
|
|
||||||
# version of a jar file you can specify it with the VERSIONS argument.
|
|
||||||
# The argument after DOC will be used for the documentation string in
|
|
||||||
# the cache.
|
|
||||||
#
|
|
||||||
# install_jar(TARGET_NAME DESTINATION)
|
|
||||||
#
|
|
||||||
# This command installs the TARGET_NAME files to the given DESTINATION.
|
|
||||||
# It should be called in the same scope as add_jar() or it will fail.
|
|
||||||
#
|
|
||||||
# install_jni_symlink(TARGET_NAME DESTINATION)
|
|
||||||
#
|
|
||||||
# This command installs the TARGET_NAME JNI symlinks to the given
|
|
||||||
# DESTINATION. It should be called in the same scope as add_jar()
|
|
||||||
# or it will fail.
|
|
||||||
#
|
|
||||||
# create_javadoc(<VAR>
|
|
||||||
# PACKAGES pkg1 [pkg2 ...]
|
|
||||||
# [SOURCEPATH <sourcepath>]
|
|
||||||
# [CLASSPATH <classpath>]
|
|
||||||
# [INSTALLPATH <install path>]
|
|
||||||
# [DOCTITLE "the documentation title"]
|
|
||||||
# [WINDOWTITLE "the title of the document"]
|
|
||||||
# [AUTHOR TRUE|FALSE]
|
|
||||||
# [USE TRUE|FALSE]
|
|
||||||
# [VERSION TRUE|FALSE]
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# Create java documentation based on files or packages. For more
|
|
||||||
# details please read the javadoc manpage.
|
|
||||||
#
|
|
||||||
# There are two main signatures for create_javadoc. The first
|
|
||||||
# signature works with package names on a path with source files:
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# create_javadoc(my_example_doc
|
|
||||||
# PACKAGES com.exmaple.foo com.example.bar
|
|
||||||
# SOURCEPATH "${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH}
|
|
||||||
# WINDOWTITLE "My example"
|
|
||||||
# DOCTITLE "<h1>My example</h1>"
|
|
||||||
# AUTHOR TRUE
|
|
||||||
# USE TRUE
|
|
||||||
# VERSION TRUE
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# The second signature for create_javadoc works on a given list of
|
|
||||||
# files.
|
|
||||||
#
|
|
||||||
# create_javadoc(<VAR>
|
|
||||||
# FILES file1 [file2 ...]
|
|
||||||
# [CLASSPATH <classpath>]
|
|
||||||
# [INSTALLPATH <install path>]
|
|
||||||
# [DOCTITLE "the documentation title"]
|
|
||||||
# [WINDOWTITLE "the title of the document"]
|
|
||||||
# [AUTHOR TRUE|FALSE]
|
|
||||||
# [USE TRUE|FALSE]
|
|
||||||
# [VERSION TRUE|FALSE]
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# create_javadoc(my_example_doc
|
|
||||||
# FILES ${example_SRCS}
|
|
||||||
# CLASSPATH ${CMAKE_JAVA_INCLUDE_PATH}
|
|
||||||
# WINDOWTITLE "My example"
|
|
||||||
# DOCTITLE "<h1>My example</h1>"
|
|
||||||
# AUTHOR TRUE
|
|
||||||
# USE TRUE
|
|
||||||
# VERSION TRUE
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# Both signatures share most of the options. These options are the
|
|
||||||
# same as what you can find in the javadoc manpage. Please look at
|
|
||||||
# the manpage for CLASSPATH, DOCTITLE, WINDOWTITLE, AUTHOR, USE and
|
|
||||||
# VERSION.
|
|
||||||
#
|
|
||||||
# The documentation will be by default installed to
|
|
||||||
#
|
|
||||||
# ${CMAKE_INSTALL_PREFIX}/share/javadoc/<VAR>
|
|
||||||
#
|
|
||||||
# if you don't set the INSTALLPATH.
|
|
||||||
#
|
|
||||||
|
|
||||||
#=============================================================================
|
|
||||||
# Copyright 2010-2011 Andreas schneider <asn@redhat.com>
|
|
||||||
# Copyright 2010 Ben Boeckel <ben.boeckel@kitware.com>
|
|
||||||
#
|
|
||||||
# Distributed under the OSI-approved BSD License (the "License");
|
|
||||||
# see accompanying file Copyright.txt for details.
|
|
||||||
#
|
|
||||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
||||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the License for more information.
|
|
||||||
#=============================================================================
|
|
||||||
# (To distribute this file outside of CMake, substitute the full
|
|
||||||
# License text for the above reference.)
|
|
||||||
|
|
||||||
function (__java_copy_file src dest comment)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${dest}
|
|
||||||
COMMAND cmake -E copy_if_different
|
|
||||||
ARGS ${src}
|
|
||||||
${dest}
|
|
||||||
DEPENDS ${src}
|
|
||||||
COMMENT ${comment})
|
|
||||||
endfunction (__java_copy_file src dest comment)
|
|
||||||
|
|
||||||
# define helper scripts
|
|
||||||
set(_JAVA_CLASS_FILELIST_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaClassFilelist.cmake)
|
|
||||||
set(_JAVA_SYMLINK_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/UseJavaSymlinks.cmake)
|
|
||||||
|
|
||||||
function(add_jar _TARGET_NAME)
|
|
||||||
set(_JAVA_SOURCE_FILES ${ARGN})
|
|
||||||
|
|
||||||
if (NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR)
|
|
||||||
set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
endif(NOT DEFINED CMAKE_JAVA_TARGET_OUTPUT_DIR)
|
|
||||||
|
|
||||||
if (CMAKE_JAVA_JAR_ENTRY_POINT)
|
|
||||||
set(_ENTRY_POINT_OPTION e)
|
|
||||||
set(_ENTRY_POINT_VALUE ${CMAKE_JAVA_JAR_ENTRY_POINT})
|
|
||||||
endif (CMAKE_JAVA_JAR_ENTRY_POINT)
|
|
||||||
|
|
||||||
if (LIBRARY_OUTPUT_PATH)
|
|
||||||
set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${LIBRARY_OUTPUT_PATH})
|
|
||||||
else (LIBRARY_OUTPUT_PATH)
|
|
||||||
set(CMAKE_JAVA_LIBRARY_OUTPUT_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR})
|
|
||||||
endif (LIBRARY_OUTPUT_PATH)
|
|
||||||
|
|
||||||
set(CMAKE_JAVA_INCLUDE_PATH
|
|
||||||
${CMAKE_JAVA_INCLUDE_PATH}
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
${CMAKE_JAVA_OBJECT_OUTPUT_PATH}
|
|
||||||
${CMAKE_JAVA_LIBRARY_OUTPUT_PATH}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (WIN32 AND NOT CYGWIN AND NOT CMAKE_CROSSCOMPILING)
|
|
||||||
set(CMAKE_JAVA_INCLUDE_FLAG_SEP ";")
|
|
||||||
else ()
|
|
||||||
set(CMAKE_JAVA_INCLUDE_FLAG_SEP ":")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
foreach (JAVA_INCLUDE_DIR ${CMAKE_JAVA_INCLUDE_PATH})
|
|
||||||
set(CMAKE_JAVA_INCLUDE_PATH_FINAL "${CMAKE_JAVA_INCLUDE_PATH_FINAL}${CMAKE_JAVA_INCLUDE_FLAG_SEP}${JAVA_INCLUDE_DIR}")
|
|
||||||
endforeach(JAVA_INCLUDE_DIR)
|
|
||||||
|
|
||||||
set(CMAKE_JAVA_CLASS_OUTPUT_PATH "${CMAKE_JAVA_TARGET_OUTPUT_DIR}${CMAKE_FILES_DIRECTORY}/${_TARGET_NAME}.dir")
|
|
||||||
|
|
||||||
set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}.jar")
|
|
||||||
if (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION)
|
|
||||||
set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar")
|
|
||||||
set(_JAVA_TARGET_OUTPUT_LINK "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar")
|
|
||||||
elseif (CMAKE_JAVA_TARGET_VERSION)
|
|
||||||
set(_JAVA_TARGET_OUTPUT_NAME "${_TARGET_NAME}-${CMAKE_JAVA_TARGET_VERSION}.jar")
|
|
||||||
set(_JAVA_TARGET_OUTPUT_LINK "${_TARGET_NAME}.jar")
|
|
||||||
elseif (CMAKE_JAVA_TARGET_OUTPUT_NAME)
|
|
||||||
set(_JAVA_TARGET_OUTPUT_NAME "${CMAKE_JAVA_TARGET_OUTPUT_NAME}.jar")
|
|
||||||
endif (CMAKE_JAVA_TARGET_OUTPUT_NAME AND CMAKE_JAVA_TARGET_VERSION)
|
|
||||||
# reset
|
|
||||||
set(CMAKE_JAVA_TARGET_OUTPUT_NAME)
|
|
||||||
|
|
||||||
set(_JAVA_CLASS_FILES)
|
|
||||||
set(_JAVA_COMPILE_FILES)
|
|
||||||
set(_JAVA_DEPENDS)
|
|
||||||
set(_JAVA_RESOURCE_FILES)
|
|
||||||
foreach(_JAVA_SOURCE_FILE ${_JAVA_SOURCE_FILES})
|
|
||||||
get_filename_component(_JAVA_EXT ${_JAVA_SOURCE_FILE} EXT)
|
|
||||||
get_filename_component(_JAVA_FILE ${_JAVA_SOURCE_FILE} NAME_WE)
|
|
||||||
get_filename_component(_JAVA_PATH ${_JAVA_SOURCE_FILE} PATH)
|
|
||||||
get_filename_component(_JAVA_FULL ${_JAVA_SOURCE_FILE} ABSOLUTE)
|
|
||||||
|
|
||||||
file(RELATIVE_PATH _JAVA_REL_BINARY_PATH ${CMAKE_JAVA_TARGET_OUTPUT_DIR} ${_JAVA_FULL})
|
|
||||||
file(RELATIVE_PATH _JAVA_REL_SOURCE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${_JAVA_FULL})
|
|
||||||
string(LENGTH ${_JAVA_REL_BINARY_PATH} _BIN_LEN)
|
|
||||||
string(LENGTH ${_JAVA_REL_SOURCE_PATH} _SRC_LEN)
|
|
||||||
if (${_BIN_LEN} LESS ${_SRC_LEN})
|
|
||||||
set(_JAVA_REL_PATH ${_JAVA_REL_BINARY_PATH})
|
|
||||||
else (${_BIN_LEN} LESS ${_SRC_LEN})
|
|
||||||
set(_JAVA_REL_PATH ${_JAVA_REL_SOURCE_PATH})
|
|
||||||
endif (${_BIN_LEN} LESS ${_SRC_LEN})
|
|
||||||
get_filename_component(_JAVA_REL_PATH ${_JAVA_REL_PATH} PATH)
|
|
||||||
|
|
||||||
if (_JAVA_EXT MATCHES ".java")
|
|
||||||
list(APPEND _JAVA_COMPILE_FILES ${_JAVA_SOURCE_FILE})
|
|
||||||
set(_JAVA_CLASS_FILE "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_REL_PATH}/${_JAVA_FILE}.class")
|
|
||||||
set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES} ${_JAVA_CLASS_FILE})
|
|
||||||
|
|
||||||
elseif (_JAVA_EXT MATCHES ".jar"
|
|
||||||
OR _JAVA_EXT MATCHES ".war"
|
|
||||||
OR _JAVA_EXT MATCHES ".ear"
|
|
||||||
OR _JAVA_EXT MATCHES ".sar")
|
|
||||||
list(APPEND CMAKE_JAVA_INCLUDE_PATH ${_JAVA_SOURCE_FILE})
|
|
||||||
|
|
||||||
elseif (_JAVA_EXT STREQUAL "")
|
|
||||||
list(APPEND CMAKE_JAVA_INCLUDE_PATH ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}} ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}_CLASSPATH})
|
|
||||||
list(APPEND _JAVA_DEPENDS ${JAVA_JAR_TARGET_${_JAVA_SOURCE_FILE}})
|
|
||||||
|
|
||||||
else (_JAVA_EXT MATCHES ".java")
|
|
||||||
__java_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/${_JAVA_SOURCE_FILE}
|
|
||||||
${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${_JAVA_SOURCE_FILE}
|
|
||||||
"Copying ${_JAVA_SOURCE_FILE} to the build directory")
|
|
||||||
list(APPEND _JAVA_RESOURCE_FILES ${_JAVA_SOURCE_FILE})
|
|
||||||
endif (_JAVA_EXT MATCHES ".java")
|
|
||||||
endforeach(_JAVA_SOURCE_FILE)
|
|
||||||
|
|
||||||
# create an empty java_class_filelist
|
|
||||||
if (NOT EXISTS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist)
|
|
||||||
file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (_JAVA_COMPILE_FILES)
|
|
||||||
# Compile the java files and create a list of class files
|
|
||||||
add_custom_command(
|
|
||||||
# NOTE: this command generates an artificial dependency file
|
|
||||||
OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
|
|
||||||
COMMAND ${Java_JAVAC_EXECUTABLE}
|
|
||||||
${CMAKE_JAVA_COMPILE_FLAGS}
|
|
||||||
-classpath "${CMAKE_JAVA_INCLUDE_PATH_FINAL}"
|
|
||||||
-d ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
|
|
||||||
${_JAVA_COMPILE_FILES}
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
|
|
||||||
DEPENDS ${_JAVA_COMPILE_FILES}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
COMMENT "Building Java objects for ${_TARGET_NAME}.jar"
|
|
||||||
)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-DCMAKE_JAVA_CLASS_OUTPUT_PATH=${CMAKE_JAVA_CLASS_OUTPUT_PATH}
|
|
||||||
-DCMAKE_JAR_CLASSES_PREFIX="${CMAKE_JAR_CLASSES_PREFIX}"
|
|
||||||
-P ${_JAVA_CLASS_FILELIST_SCRIPT}
|
|
||||||
DEPENDS ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_compiled_${_TARGET_NAME}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
endif (_JAVA_COMPILE_FILES)
|
|
||||||
|
|
||||||
# create the jar file
|
|
||||||
set(_JAVA_JAR_OUTPUT_PATH
|
|
||||||
${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_NAME})
|
|
||||||
if (CMAKE_JNI_TARGET)
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${_JAVA_JAR_OUTPUT_PATH}
|
|
||||||
COMMAND ${Java_JAR_EXECUTABLE}
|
|
||||||
-cf${_ENTRY_POINT_OPTION} ${_JAVA_JAR_OUTPUT_PATH} ${_ENTRY_POINT_VALUE}
|
|
||||||
${_JAVA_RESOURCE_FILES} @java_class_filelist
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
|
|
||||||
-D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_TARGET_OUTPUT_NAME}
|
|
||||||
-D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
|
|
||||||
-P ${_JAVA_SYMLINK_SCRIPT}
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
|
|
||||||
-D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_JAR_OUTPUT_PATH}
|
|
||||||
-D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
|
|
||||||
-P ${_JAVA_SYMLINK_SCRIPT}
|
|
||||||
DEPENDS ${_JAVA_RESOURCE_FILES} ${_JAVA_DEPENDS} ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
|
|
||||||
WORKING_DIRECTORY ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
|
|
||||||
COMMENT "Creating Java archive ${_JAVA_TARGET_OUTPUT_NAME}"
|
|
||||||
)
|
|
||||||
else ()
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${_JAVA_JAR_OUTPUT_PATH}
|
|
||||||
COMMAND ${Java_JAR_EXECUTABLE}
|
|
||||||
-cf${_ENTRY_POINT_OPTION} ${_JAVA_JAR_OUTPUT_PATH} ${_ENTRY_POINT_VALUE}
|
|
||||||
${_JAVA_RESOURCE_FILES} @java_class_filelist
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
-D_JAVA_TARGET_DIR=${CMAKE_JAVA_TARGET_OUTPUT_DIR}
|
|
||||||
-D_JAVA_TARGET_OUTPUT_NAME=${_JAVA_TARGET_OUTPUT_NAME}
|
|
||||||
-D_JAVA_TARGET_OUTPUT_LINK=${_JAVA_TARGET_OUTPUT_LINK}
|
|
||||||
-P ${_JAVA_SYMLINK_SCRIPT}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_JAVA_CLASS_OUTPUT_PATH}
|
|
||||||
DEPENDS ${_JAVA_RESOURCE_FILES} ${_JAVA_DEPENDS} ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist
|
|
||||||
COMMENT "Creating Java archive ${_JAVA_TARGET_OUTPUT_NAME}"
|
|
||||||
)
|
|
||||||
endif (CMAKE_JNI_TARGET)
|
|
||||||
|
|
||||||
# Add the target and make sure we have the latest resource files.
|
|
||||||
add_custom_target(${_TARGET_NAME} ALL DEPENDS ${_JAVA_JAR_OUTPUT_PATH})
|
|
||||||
|
|
||||||
set_property(
|
|
||||||
TARGET
|
|
||||||
${_TARGET_NAME}
|
|
||||||
PROPERTY
|
|
||||||
INSTALL_FILES
|
|
||||||
${_JAVA_JAR_OUTPUT_PATH}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (_JAVA_TARGET_OUTPUT_LINK)
|
|
||||||
set_property(
|
|
||||||
TARGET
|
|
||||||
${_TARGET_NAME}
|
|
||||||
PROPERTY
|
|
||||||
INSTALL_FILES
|
|
||||||
${_JAVA_JAR_OUTPUT_PATH}
|
|
||||||
${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_LINK}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (CMAKE_JNI_TARGET)
|
|
||||||
set_property(
|
|
||||||
TARGET
|
|
||||||
${_TARGET_NAME}
|
|
||||||
PROPERTY
|
|
||||||
JNI_SYMLINK
|
|
||||||
${CMAKE_JAVA_TARGET_OUTPUT_DIR}/${_JAVA_TARGET_OUTPUT_LINK}
|
|
||||||
)
|
|
||||||
endif (CMAKE_JNI_TARGET)
|
|
||||||
endif (_JAVA_TARGET_OUTPUT_LINK)
|
|
||||||
|
|
||||||
set_property(
|
|
||||||
TARGET
|
|
||||||
${_TARGET_NAME}
|
|
||||||
PROPERTY
|
|
||||||
JAR_FILE
|
|
||||||
${_JAVA_JAR_OUTPUT_PATH}
|
|
||||||
)
|
|
||||||
|
|
||||||
set_property(
|
|
||||||
TARGET
|
|
||||||
${_TARGET_NAME}
|
|
||||||
PROPERTY
|
|
||||||
CLASSDIR
|
|
||||||
${CMAKE_JAVA_CLASS_OUTPUT_PATH}
|
|
||||||
)
|
|
||||||
|
|
||||||
endfunction(add_jar)
|
|
||||||
|
|
||||||
function(INSTALL_JAR _TARGET_NAME _DESTINATION)
|
|
||||||
get_property(__FILES
|
|
||||||
TARGET
|
|
||||||
${_TARGET_NAME}
|
|
||||||
PROPERTY
|
|
||||||
INSTALL_FILES
|
|
||||||
)
|
|
||||||
|
|
||||||
if (__FILES)
|
|
||||||
install(
|
|
||||||
FILES
|
|
||||||
${__FILES}
|
|
||||||
DESTINATION
|
|
||||||
${_DESTINATION}
|
|
||||||
)
|
|
||||||
else (__FILES)
|
|
||||||
message(SEND_ERROR "The target ${_TARGET_NAME} is not known in this scope.")
|
|
||||||
endif (__FILES)
|
|
||||||
endfunction(INSTALL_JAR _TARGET_NAME _DESTINATION)
|
|
||||||
|
|
||||||
function(INSTALL_JNI_SYMLINK _TARGET_NAME _DESTINATION)
|
|
||||||
get_property(__SYMLINK
|
|
||||||
TARGET
|
|
||||||
${_TARGET_NAME}
|
|
||||||
PROPERTY
|
|
||||||
JNI_SYMLINK
|
|
||||||
)
|
|
||||||
|
|
||||||
if (__SYMLINK)
|
|
||||||
install(
|
|
||||||
FILES
|
|
||||||
${__SYMLINK}
|
|
||||||
DESTINATION
|
|
||||||
${_DESTINATION}
|
|
||||||
)
|
|
||||||
else (__SYMLINK)
|
|
||||||
message(SEND_ERROR "The target ${_TARGET_NAME} is not known in this scope.")
|
|
||||||
endif (__SYMLINK)
|
|
||||||
endfunction(INSTALL_JNI_SYMLINK _TARGET_NAME _DESTINATION)
|
|
||||||
|
|
||||||
function (find_jar VARIABLE)
|
|
||||||
set(_jar_names)
|
|
||||||
set(_jar_files)
|
|
||||||
set(_jar_versions)
|
|
||||||
set(_jar_paths
|
|
||||||
/usr/share/java/
|
|
||||||
/usr/local/share/java/
|
|
||||||
${Java_JAR_PATHS})
|
|
||||||
set(_jar_doc "NOTSET")
|
|
||||||
|
|
||||||
set(_state "name")
|
|
||||||
|
|
||||||
foreach (arg ${ARGN})
|
|
||||||
if (${_state} STREQUAL "name")
|
|
||||||
if (${arg} STREQUAL "VERSIONS")
|
|
||||||
set(_state "versions")
|
|
||||||
elseif (${arg} STREQUAL "NAMES")
|
|
||||||
set(_state "names")
|
|
||||||
elseif (${arg} STREQUAL "PATHS")
|
|
||||||
set(_state "paths")
|
|
||||||
elseif (${arg} STREQUAL "DOC")
|
|
||||||
set(_state "doc")
|
|
||||||
else (${arg} STREQUAL "NAMES")
|
|
||||||
set(_jar_names ${arg})
|
|
||||||
if (_jar_doc STREQUAL "NOTSET")
|
|
||||||
set(_jar_doc "Finding ${arg} jar")
|
|
||||||
endif (_jar_doc STREQUAL "NOTSET")
|
|
||||||
endif (${arg} STREQUAL "VERSIONS")
|
|
||||||
elseif (${_state} STREQUAL "versions")
|
|
||||||
if (${arg} STREQUAL "NAMES")
|
|
||||||
set(_state "names")
|
|
||||||
elseif (${arg} STREQUAL "PATHS")
|
|
||||||
set(_state "paths")
|
|
||||||
elseif (${arg} STREQUAL "DOC")
|
|
||||||
set(_state "doc")
|
|
||||||
else (${arg} STREQUAL "NAMES")
|
|
||||||
set(_jar_versions ${_jar_versions} ${arg})
|
|
||||||
endif (${arg} STREQUAL "NAMES")
|
|
||||||
elseif (${_state} STREQUAL "names")
|
|
||||||
if (${arg} STREQUAL "VERSIONS")
|
|
||||||
set(_state "versions")
|
|
||||||
elseif (${arg} STREQUAL "PATHS")
|
|
||||||
set(_state "paths")
|
|
||||||
elseif (${arg} STREQUAL "DOC")
|
|
||||||
set(_state "doc")
|
|
||||||
else (${arg} STREQUAL "VERSIONS")
|
|
||||||
set(_jar_names ${_jar_names} ${arg})
|
|
||||||
if (_jar_doc STREQUAL "NOTSET")
|
|
||||||
set(_jar_doc "Finding ${arg} jar")
|
|
||||||
endif (_jar_doc STREQUAL "NOTSET")
|
|
||||||
endif (${arg} STREQUAL "VERSIONS")
|
|
||||||
elseif (${_state} STREQUAL "paths")
|
|
||||||
if (${arg} STREQUAL "VERSIONS")
|
|
||||||
set(_state "versions")
|
|
||||||
elseif (${arg} STREQUAL "NAMES")
|
|
||||||
set(_state "names")
|
|
||||||
elseif (${arg} STREQUAL "DOC")
|
|
||||||
set(_state "doc")
|
|
||||||
else (${arg} STREQUAL "VERSIONS")
|
|
||||||
set(_jar_paths ${_jar_paths} ${arg})
|
|
||||||
endif (${arg} STREQUAL "VERSIONS")
|
|
||||||
elseif (${_state} STREQUAL "doc")
|
|
||||||
if (${arg} STREQUAL "VERSIONS")
|
|
||||||
set(_state "versions")
|
|
||||||
elseif (${arg} STREQUAL "NAMES")
|
|
||||||
set(_state "names")
|
|
||||||
elseif (${arg} STREQUAL "PATHS")
|
|
||||||
set(_state "paths")
|
|
||||||
else (${arg} STREQUAL "VERSIONS")
|
|
||||||
set(_jar_doc ${arg})
|
|
||||||
endif (${arg} STREQUAL "VERSIONS")
|
|
||||||
endif (${_state} STREQUAL "name")
|
|
||||||
endforeach (arg ${ARGN})
|
|
||||||
|
|
||||||
if (NOT _jar_names)
|
|
||||||
message(FATAL_ERROR "find_jar: No name to search for given")
|
|
||||||
endif (NOT _jar_names)
|
|
||||||
|
|
||||||
foreach (jar_name ${_jar_names})
|
|
||||||
foreach (version ${_jar_versions})
|
|
||||||
set(_jar_files ${_jar_files} ${jar_name}-${version}.jar)
|
|
||||||
endforeach (version ${_jar_versions})
|
|
||||||
set(_jar_files ${_jar_files} ${jar_name}.jar)
|
|
||||||
endforeach (jar_name ${_jar_names})
|
|
||||||
|
|
||||||
find_file(${VARIABLE}
|
|
||||||
NAMES ${_jar_files}
|
|
||||||
PATHS ${_jar_paths}
|
|
||||||
DOC ${_jar_doc}
|
|
||||||
NO_DEFAULT_PATH)
|
|
||||||
endfunction (find_jar VARIABLE)
|
|
||||||
|
|
||||||
function(create_javadoc _target)
|
|
||||||
set(_javadoc_packages)
|
|
||||||
set(_javadoc_files)
|
|
||||||
set(_javadoc_sourcepath)
|
|
||||||
set(_javadoc_classpath)
|
|
||||||
set(_javadoc_installpath "${CMAKE_INSTALL_PREFIX}/share/javadoc")
|
|
||||||
set(_javadoc_doctitle)
|
|
||||||
set(_javadoc_windowtitle)
|
|
||||||
set(_javadoc_author FALSE)
|
|
||||||
set(_javadoc_version FALSE)
|
|
||||||
set(_javadoc_use FALSE)
|
|
||||||
|
|
||||||
set(_state "package")
|
|
||||||
|
|
||||||
foreach (arg ${ARGN})
|
|
||||||
if (${_state} STREQUAL "package")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
set(_javadoc_packages ${arg})
|
|
||||||
set(_state "packages")
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "packages")
|
|
||||||
if (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
list(APPEND _javadoc_packages ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "files")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
list(APPEND _javadoc_files ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "sourcepath")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
list(APPEND _javadoc_sourcepath ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "classpath")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
list(APPEND _javadoc_classpath ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "installpath")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
set(_javadoc_installpath ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "doctitle")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
set(_javadoc_doctitle ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "windowtitle")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
set(_javadoc_windowtitle ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "author")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
set(_javadoc_author ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "use")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
set(_javadoc_use ${arg})
|
|
||||||
endif ()
|
|
||||||
elseif (${_state} STREQUAL "version")
|
|
||||||
if (${arg} STREQUAL "PACKAGES")
|
|
||||||
set(_state "packages")
|
|
||||||
elseif (${arg} STREQUAL "FILES")
|
|
||||||
set(_state "files")
|
|
||||||
elseif (${arg} STREQUAL "SOURCEPATH")
|
|
||||||
set(_state "sourcepath")
|
|
||||||
elseif (${arg} STREQUAL "CLASSPATH")
|
|
||||||
set(_state "classpath")
|
|
||||||
elseif (${arg} STREQUAL "INSTALLPATH")
|
|
||||||
set(_state "installpath")
|
|
||||||
elseif (${arg} STREQUAL "DOCTITLE")
|
|
||||||
set(_state "doctitle")
|
|
||||||
elseif (${arg} STREQUAL "WINDOWTITLE")
|
|
||||||
set(_state "windowtitle")
|
|
||||||
elseif (${arg} STREQUAL "AUTHOR")
|
|
||||||
set(_state "author")
|
|
||||||
elseif (${arg} STREQUAL "USE")
|
|
||||||
set(_state "use")
|
|
||||||
elseif (${arg} STREQUAL "VERSION")
|
|
||||||
set(_state "version")
|
|
||||||
else ()
|
|
||||||
set(_javadoc_version ${arg})
|
|
||||||
endif ()
|
|
||||||
endif (${_state} STREQUAL "package")
|
|
||||||
endforeach (arg ${ARGN})
|
|
||||||
|
|
||||||
set(_javadoc_builddir ${CMAKE_CURRENT_BINARY_DIR}/javadoc/${_target})
|
|
||||||
set(_javadoc_options -d ${_javadoc_builddir})
|
|
||||||
|
|
||||||
if (_javadoc_sourcepath)
|
|
||||||
set(_start TRUE)
|
|
||||||
foreach(_path ${_javadoc_sourcepath})
|
|
||||||
if (_start)
|
|
||||||
set(_sourcepath ${_path})
|
|
||||||
set(_start FALSE)
|
|
||||||
else (_start)
|
|
||||||
set(_sourcepath ${_sourcepath}:${_path})
|
|
||||||
endif (_start)
|
|
||||||
endforeach(_path ${_javadoc_sourcepath})
|
|
||||||
set(_javadoc_options ${_javadoc_options} -sourcepath ${_sourcepath})
|
|
||||||
endif (_javadoc_sourcepath)
|
|
||||||
|
|
||||||
if (_javadoc_classpath)
|
|
||||||
set(_start TRUE)
|
|
||||||
foreach(_path ${_javadoc_classpath})
|
|
||||||
if (_start)
|
|
||||||
set(_classpath ${_path})
|
|
||||||
set(_start FALSE)
|
|
||||||
else (_start)
|
|
||||||
set(_classpath ${_classpath}:${_path})
|
|
||||||
endif (_start)
|
|
||||||
endforeach(_path ${_javadoc_classpath})
|
|
||||||
set(_javadoc_options ${_javadoc_options} -classpath "${_classpath}")
|
|
||||||
endif (_javadoc_classpath)
|
|
||||||
|
|
||||||
if (_javadoc_doctitle)
|
|
||||||
set(_javadoc_options ${_javadoc_options} -doctitle '${_javadoc_doctitle}')
|
|
||||||
endif (_javadoc_doctitle)
|
|
||||||
|
|
||||||
if (_javadoc_windowtitle)
|
|
||||||
set(_javadoc_options ${_javadoc_options} -windowtitle '${_javadoc_windowtitle}')
|
|
||||||
endif (_javadoc_windowtitle)
|
|
||||||
|
|
||||||
if (_javadoc_author)
|
|
||||||
set(_javadoc_options ${_javadoc_options} -author)
|
|
||||||
endif (_javadoc_author)
|
|
||||||
|
|
||||||
if (_javadoc_use)
|
|
||||||
set(_javadoc_options ${_javadoc_options} -use)
|
|
||||||
endif (_javadoc_use)
|
|
||||||
|
|
||||||
if (_javadoc_version)
|
|
||||||
set(_javadoc_options ${_javadoc_options} -version)
|
|
||||||
endif (_javadoc_version)
|
|
||||||
|
|
||||||
add_custom_target(${_target}_javadoc ALL
|
|
||||||
COMMAND ${Java_JAVADOC_EXECUTABLE} ${_javadoc_options}
|
|
||||||
${_javadoc_files}
|
|
||||||
${_javadoc_packages}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
install(
|
|
||||||
DIRECTORY ${_javadoc_builddir}
|
|
||||||
DESTINATION ${_javadoc_installpath}
|
|
||||||
)
|
|
||||||
endfunction(create_javadoc)
|
|
@ -1,52 +0,0 @@
|
|||||||
#
|
|
||||||
# This script create a list of compiled Java class files to be added to a
|
|
||||||
# jar file. This avoids including cmake files which get created in the
|
|
||||||
# binary directory.
|
|
||||||
#
|
|
||||||
|
|
||||||
#=============================================================================
|
|
||||||
# Copyright 2010-2011 Andreas schneider <asn@redhat.com>
|
|
||||||
#
|
|
||||||
# Distributed under the OSI-approved BSD License (the "License");
|
|
||||||
# see accompanying file Copyright.txt for details.
|
|
||||||
#
|
|
||||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
||||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the License for more information.
|
|
||||||
#=============================================================================
|
|
||||||
# (To distribute this file outside of CMake, substitute the full
|
|
||||||
# License text for the above reference.)
|
|
||||||
|
|
||||||
if (CMAKE_JAVA_CLASS_OUTPUT_PATH)
|
|
||||||
if (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
|
|
||||||
|
|
||||||
set(_JAVA_GLOBBED_FILES)
|
|
||||||
if (CMAKE_JAR_CLASSES_PREFIX)
|
|
||||||
foreach(JAR_CLASS_PREFIX ${CMAKE_JAR_CLASSES_PREFIX})
|
|
||||||
message(STATUS "JAR_CLASS_PREFIX: ${JAR_CLASS_PREFIX}")
|
|
||||||
|
|
||||||
file(GLOB_RECURSE _JAVA_GLOBBED_TMP_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/${JAR_CLASS_PREFIX}/*.class")
|
|
||||||
if (_JAVA_GLOBBED_TMP_FILES)
|
|
||||||
list(APPEND _JAVA_GLOBBED_FILES ${_JAVA_GLOBBED_TMP_FILES})
|
|
||||||
endif (_JAVA_GLOBBED_TMP_FILES)
|
|
||||||
endforeach(JAR_CLASS_PREFIX ${CMAKE_JAR_CLASSES_PREFIX})
|
|
||||||
else()
|
|
||||||
file(GLOB_RECURSE _JAVA_GLOBBED_FILES "${CMAKE_JAVA_CLASS_OUTPUT_PATH}/*.class")
|
|
||||||
endif (CMAKE_JAR_CLASSES_PREFIX)
|
|
||||||
|
|
||||||
set(_JAVA_CLASS_FILES)
|
|
||||||
# file(GLOB_RECURSE foo RELATIVE) is broken so we need this.
|
|
||||||
foreach(_JAVA_GLOBBED_FILE ${_JAVA_GLOBBED_FILES})
|
|
||||||
file(RELATIVE_PATH _JAVA_CLASS_FILE ${CMAKE_JAVA_CLASS_OUTPUT_PATH} ${_JAVA_GLOBBED_FILE})
|
|
||||||
set(_JAVA_CLASS_FILES ${_JAVA_CLASS_FILES}${_JAVA_CLASS_FILE}\n)
|
|
||||||
endforeach(_JAVA_GLOBBED_FILE ${_JAVA_GLOBBED_FILES})
|
|
||||||
|
|
||||||
# write to file
|
|
||||||
file(WRITE ${CMAKE_JAVA_CLASS_OUTPUT_PATH}/java_class_filelist ${_JAVA_CLASS_FILES})
|
|
||||||
|
|
||||||
else (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
|
|
||||||
message(SEND_ERROR "FATAL: Java class output path doesn't exist")
|
|
||||||
endif (EXISTS "${CMAKE_JAVA_CLASS_OUTPUT_PATH}")
|
|
||||||
else (CMAKE_JAVA_CLASS_OUTPUT_PATH)
|
|
||||||
message(SEND_ERROR "FATAL: Can't find CMAKE_JAVA_CLASS_OUTPUT_PATH")
|
|
||||||
endif (CMAKE_JAVA_CLASS_OUTPUT_PATH)
|
|
@ -1,32 +0,0 @@
|
|||||||
#
|
|
||||||
# Helper script for UseJava.cmake
|
|
||||||
#
|
|
||||||
|
|
||||||
#=============================================================================
|
|
||||||
# Copyright 2010-2011 Andreas schneider <asn@redhat.com>
|
|
||||||
#
|
|
||||||
# Distributed under the OSI-approved BSD License (the "License");
|
|
||||||
# see accompanying file Copyright.txt for details.
|
|
||||||
#
|
|
||||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
|
||||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the License for more information.
|
|
||||||
#=============================================================================
|
|
||||||
# (To distribute this file outside of CMake, substitute the full
|
|
||||||
# License text for the above reference.)
|
|
||||||
|
|
||||||
if (UNIX AND _JAVA_TARGET_OUTPUT_LINK)
|
|
||||||
if (_JAVA_TARGET_OUTPUT_NAME)
|
|
||||||
find_program(LN_EXECUTABLE
|
|
||||||
NAMES
|
|
||||||
ln
|
|
||||||
)
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${LN_EXECUTABLE} -sf "${_JAVA_TARGET_OUTPUT_NAME}" "${_JAVA_TARGET_OUTPUT_LINK}"
|
|
||||||
WORKING_DIRECTORY ${_JAVA_TARGET_DIR}
|
|
||||||
)
|
|
||||||
else (_JAVA_TARGET_OUTPUT_NAME)
|
|
||||||
message(SEND_ERROR "FATAL: Can't find _JAVA_TARGET_OUTPUT_NAME")
|
|
||||||
endif (_JAVA_TARGET_OUTPUT_NAME)
|
|
||||||
endif (UNIX AND _JAVA_TARGET_OUTPUT_LINK)
|
|
15
default.nix
15
default.nix
@ -1 +1,14 @@
|
|||||||
(import packages/nix/flake-compat.nix).defaultNix
|
(
|
||||||
|
import
|
||||||
|
(
|
||||||
|
let
|
||||||
|
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||||
|
in
|
||||||
|
fetchTarball {
|
||||||
|
url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||||
|
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{src = ./.;}
|
||||||
|
)
|
||||||
|
.defaultNix
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
POLYMC(1)
|
|
||||||
==========
|
|
||||||
:doctype: manpage
|
|
||||||
|
|
||||||
|
|
||||||
NAME
|
|
||||||
----
|
|
||||||
polymc - a launcher and instance manager for Minecraft.
|
|
||||||
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
--------
|
|
||||||
*polymc* ['OPTIONS']
|
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
-----------
|
|
||||||
PolyMC is a custom launcher for Minecraft that allows you to easily manage
|
|
||||||
multiple installations of Minecraft at once. It also allows you to easily
|
|
||||||
install and remove mods by simply dragging and dropping.
|
|
||||||
Here are the current features of PolyMC.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-------
|
|
||||||
*-d, --dir*='DIRECTORY'::
|
|
||||||
Use 'DIRECTORY' as the PolyMC root.
|
|
||||||
|
|
||||||
*-l, --launch*='INSTANCE_ID'::
|
|
||||||
Launch the instance specified by 'INSTANCE_ID'.
|
|
||||||
|
|
||||||
*--alive*::
|
|
||||||
Write a small 'live.check' file after PolyMC starts.
|
|
||||||
|
|
||||||
*-h, --help*::
|
|
||||||
Display help text and exit.
|
|
||||||
|
|
||||||
*-v, --version*::
|
|
||||||
Display program version and exit.
|
|
||||||
*-a, --profile*='PROFILE'::
|
|
||||||
Use the account specified by 'PROFILE' (only valid in combination with --launch).
|
|
||||||
|
|
||||||
EXIT STATUS
|
|
||||||
-----------
|
|
||||||
*0*::
|
|
||||||
Success
|
|
||||||
|
|
||||||
*1*::
|
|
||||||
Failure (syntax or usage error; configuration error; unexpected error).
|
|
||||||
|
|
||||||
BUGS
|
|
||||||
----
|
|
||||||
<https://github.com/PolyMC/PolyMC/issues>
|
|
||||||
|
|
||||||
RESOURCES
|
|
||||||
---------
|
|
||||||
GitHub: <https://github.com/PolyMC/PolyMC>
|
|
||||||
|
|
||||||
Main website: <https://polymc.org>
|
|
||||||
|
|
||||||
AUTHORS
|
|
||||||
-------
|
|
||||||
peterix <peterix@gmail.com>
|
|
||||||
|
|
||||||
swurl <swurl@swurl.xyz>
|
|
||||||
|
|
||||||
// vim: syntax=asciidoc
|
|
119
flake.lock
generated
119
flake.lock
generated
@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1641205782,
|
"lastModified": 1673956053,
|
||||||
"narHash": "sha256-4jY7RCWUoZ9cKD8co0/4tFARpWB+57+r1bLLvXNJliY=",
|
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "b7547d3eed6f32d06102ead8991ec52ab0a4f1a7",
|
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -16,13 +16,31 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-parts": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1683560683,
|
||||||
|
"narHash": "sha256-XAygPMN5Xnk/W2c1aW0jyEa6lfMDZWlQgiNtmHXytPc=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"rev": "006c75898cf814ef9497252b022e91c946ba8e17",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "flake-parts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"flake-utils": {
|
"flake-utils": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1642700792,
|
"lastModified": 1667395993,
|
||||||
"narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=",
|
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "846b2ae0fc4cc943637d3d1def4454213e203cba",
|
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -31,61 +49,112 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"gitignore": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"pre-commit-hooks",
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1660459072,
|
||||||
|
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "hercules-ci",
|
||||||
|
"repo": "gitignore.nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"libnbtplusplus": {
|
"libnbtplusplus": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1591558203,
|
"lastModified": 1650031308,
|
||||||
"narHash": "sha256-QgvNvaoFflCXEPCCFBCeZvYTpuiwScBG7EosUgFwFNQ=",
|
"narHash": "sha256-TvVOjkUobYJD9itQYueELJX3wmecvEdCbJ0FinW2mL4=",
|
||||||
"owner": "multimc",
|
"owner": "PrismLauncher",
|
||||||
"repo": "libnbtplusplus",
|
"repo": "libnbtplusplus",
|
||||||
"rev": "dc72a20b7efd304d12af2025223fad07b4b78464",
|
"rev": "2203af7eeb48c45398139b583615134efd8d407f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "multimc",
|
"owner": "PrismLauncher",
|
||||||
"repo": "libnbtplusplus",
|
"repo": "libnbtplusplus",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1643169865,
|
"lastModified": 1685012353,
|
||||||
"narHash": "sha256-+KIpNRazbc8Gac9jdWCKQkFv9bjceaLaLhlwqUEYu8c=",
|
"narHash": "sha256-U3oOge4cHnav8OLGdRVhL45xoRj4Ppd+It6nPC9nNIU=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "945ec499041db73043f745fad3b2a3a01e826081",
|
"rev": "aeb75dba965e790de427b73315d5addf91a54955",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-lib": {
|
||||||
|
"locked": {
|
||||||
|
"dir": "lib",
|
||||||
|
"lastModified": 1682879489,
|
||||||
|
"narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"dir": "lib",
|
||||||
|
"owner": "NixOS",
|
||||||
"ref": "nixos-unstable",
|
"ref": "nixos-unstable",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"quazip": {
|
"pre-commit-hooks": {
|
||||||
"flake": false,
|
"inputs": {
|
||||||
|
"flake-compat": [
|
||||||
|
"flake-compat"
|
||||||
|
],
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"gitignore": "gitignore",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"nixpkgs-stable": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1643049383,
|
"lastModified": 1684842236,
|
||||||
"narHash": "sha256-LcJY6yd6GyeL7X5MP4L94diceM1TYespWByliBsjK98=",
|
"narHash": "sha256-rYWsIXHvNhVQ15RQlBUv67W3YnM+Pd+DuXGMvCBq2IE=",
|
||||||
"owner": "stachenov",
|
"owner": "cachix",
|
||||||
"repo": "quazip",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"rev": "09ec1d10c6d627f895109b21728dda000cbfa7d1",
|
"rev": "61e567d6497bc9556f391faebe5e410e6623217f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "stachenov",
|
"owner": "cachix",
|
||||||
"repo": "quazip",
|
"repo": "pre-commit-hooks.nix",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"flake-utils": "flake-utils",
|
"flake-parts": "flake-parts",
|
||||||
"libnbtplusplus": "libnbtplusplus",
|
"libnbtplusplus": "libnbtplusplus",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"quazip": "quazip"
|
"pre-commit-hooks": "pre-commit-hooks"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
59
flake.nix
59
flake.nix
@ -1,50 +1,27 @@
|
|||||||
{
|
{
|
||||||
description = "PolyMC flake";
|
description = "A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once (Fork of MultiMC)";
|
||||||
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
|
||||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
inputs = {
|
||||||
inputs.flake-compat = {
|
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||||
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||||
|
pre-commit-hooks = {
|
||||||
|
url = "github:cachix/pre-commit-hooks.nix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
inputs.nixpkgs-stable.follows = "nixpkgs";
|
||||||
|
inputs.flake-compat.follows = "flake-compat";
|
||||||
|
};
|
||||||
|
flake-compat = {
|
||||||
url = "github:edolstra/flake-compat";
|
url = "github:edolstra/flake-compat";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
inputs.libnbtplusplus = {
|
libnbtplusplus = {
|
||||||
url = "github:multimc/libnbtplusplus";
|
url = "github:PrismLauncher/libnbtplusplus";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
inputs.quazip = {
|
|
||||||
url = "github:stachenov/quazip";
|
|
||||||
flake = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = args@{ self, nixpkgs, flake-utils, libnbtplusplus, quazip, ... }:
|
outputs = inputs:
|
||||||
let
|
inputs.flake-parts.lib.mkFlake
|
||||||
systems = [
|
{inherit inputs;}
|
||||||
"aarch64-linux"
|
{imports = [./nix];};
|
||||||
# "aarch64-darwin" # qtbase is currently broken
|
|
||||||
"i686-linux"
|
|
||||||
"x86_64-darwin"
|
|
||||||
"x86_64-linux"
|
|
||||||
];
|
|
||||||
in {
|
|
||||||
overlay = final: prev: {
|
|
||||||
inherit (self.packages.${final.system}) polymc;
|
|
||||||
};
|
|
||||||
} // flake-utils.lib.eachSystem systems (system:
|
|
||||||
let pkgs = import nixpkgs { inherit system; };
|
|
||||||
in {
|
|
||||||
packages = {
|
|
||||||
polymc = pkgs.libsForQt5.callPackage ./packages/nix/polymc {
|
|
||||||
inherit self;
|
|
||||||
submoduleQuazip = quazip;
|
|
||||||
submoduleNbt = libnbtplusplus;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
apps = {
|
|
||||||
polymc = flake-utils.lib.mkApp {
|
|
||||||
name = "polymc";
|
|
||||||
drv = self.packages.${system}.polymc;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
defaultPackage = self.packages.${system}.polymc;
|
|
||||||
defaultApp = self.apps.${system}.polymc;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
85
flatpak/org.prismlauncher.PrismLauncher.yml
Normal file
85
flatpak/org.prismlauncher.PrismLauncher.yml
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
id: org.prismlauncher.PrismLauncher
|
||||||
|
runtime: org.kde.Platform
|
||||||
|
runtime-version: "5.15-22.08"
|
||||||
|
sdk: org.kde.Sdk
|
||||||
|
sdk-extensions:
|
||||||
|
- org.freedesktop.Sdk.Extension.openjdk17
|
||||||
|
- org.freedesktop.Sdk.Extension.openjdk8
|
||||||
|
add-extensions:
|
||||||
|
com.valvesoftware.Steam.Utility.gamescope:
|
||||||
|
version: stable
|
||||||
|
add-ld-path: lib
|
||||||
|
no-autodownload: true
|
||||||
|
autodelete: false
|
||||||
|
directory: utils/gamescope
|
||||||
|
|
||||||
|
command: prismlauncher
|
||||||
|
finish-args:
|
||||||
|
- --share=ipc
|
||||||
|
- --socket=x11
|
||||||
|
- --socket=wayland
|
||||||
|
- --device=all
|
||||||
|
- --share=network
|
||||||
|
- --socket=pulseaudio
|
||||||
|
# for Discord RPC mods
|
||||||
|
- --filesystem=xdg-run/app/com.discordapp.Discord:create
|
||||||
|
# Mod drag&drop
|
||||||
|
- --filesystem=xdg-download:ro
|
||||||
|
|
||||||
|
modules:
|
||||||
|
- name: prismlauncher
|
||||||
|
buildsystem: cmake-ninja
|
||||||
|
config-opts:
|
||||||
|
- -DLauncher_BUILD_PLATFORM=flatpak
|
||||||
|
- -DCMAKE_BUILD_TYPE=Debug
|
||||||
|
- -DLauncher_QT_VERSION_MAJOR=5
|
||||||
|
build-options:
|
||||||
|
env:
|
||||||
|
JAVA_HOME: /usr/lib/sdk/openjdk17/jvm/openjdk-17
|
||||||
|
JAVA_COMPILER: /usr/lib/sdk/openjdk17/jvm/openjdk-17/bin/javac
|
||||||
|
sources:
|
||||||
|
- type: dir
|
||||||
|
path: ../
|
||||||
|
builddir: true
|
||||||
|
- name: openjdk
|
||||||
|
buildsystem: simple
|
||||||
|
build-commands:
|
||||||
|
- mkdir -p /app/jdk/
|
||||||
|
- /usr/lib/sdk/openjdk17/install.sh
|
||||||
|
- mv /app/jre /app/jdk/17
|
||||||
|
- /usr/lib/sdk/openjdk8/install.sh
|
||||||
|
- mv /app/jre /app/jdk/8
|
||||||
|
cleanup: [/jre]
|
||||||
|
- name: xrandr
|
||||||
|
buildsystem: autotools
|
||||||
|
sources:
|
||||||
|
- type: archive
|
||||||
|
url: https://xorg.freedesktop.org/archive/individual/app/xrandr-1.5.1.tar.xz
|
||||||
|
sha256: 7bc76daf9d72f8aff885efad04ce06b90488a1a169d118dea8a2b661832e8762
|
||||||
|
cleanup: [/share/man, /bin/xkeystone]
|
||||||
|
- name: gamemode
|
||||||
|
buildsystem: meson
|
||||||
|
config-opts:
|
||||||
|
- -Dwith-sd-bus-provider=no-daemon
|
||||||
|
- -Dwith-examples=false
|
||||||
|
post-install:
|
||||||
|
# gamemoderun is installed for users who want to use wrapper commands
|
||||||
|
# post-install is running inside the build dir, we need it from the source though
|
||||||
|
- install -Dm755 ../data/gamemoderun -t /app/bin
|
||||||
|
sources:
|
||||||
|
- type: git
|
||||||
|
url: https://github.com/FeralInteractive/gamemode
|
||||||
|
tag: "1.7"
|
||||||
|
commit: 4dc99dff76218718763a6b07fc1900fa6d1dafd9
|
||||||
|
- name: enhance
|
||||||
|
buildsystem: simple
|
||||||
|
build-commands:
|
||||||
|
- mkdir -p /app/utils/gamescope
|
||||||
|
- install -Dm755 prime-run /app/bin/prime-run
|
||||||
|
- mv /app/bin/prismlauncher /app/bin/prismrun
|
||||||
|
- install -Dm755 prismlauncher /app/bin/prismlauncher
|
||||||
|
sources:
|
||||||
|
- type: file
|
||||||
|
path: ../flatpak/prime-run
|
||||||
|
- type: file
|
||||||
|
path: ../flatpak/prismlauncher
|
4
flatpak/prime-run
Normal file
4
flatpak/prime-run
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export __NV_PRIME_RENDER_OFFLOAD=1 __VK_LAYER_NV_optimus=NVIDIA_only __GLX_VENDOR_LIBRARY_NAME=nvidia
|
||||||
|
exec "$@"
|
11
flatpak/prismlauncher
Normal file
11
flatpak/prismlauncher
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# discord RPC
|
||||||
|
for i in {0..9}; do
|
||||||
|
test -S "$XDG_RUNTIME_DIR"/discord-ipc-"$i" || ln -sf {app/com.discordapp.Discord,"$XDG_RUNTIME_DIR"}/discord-ipc-"$i";
|
||||||
|
done
|
||||||
|
|
||||||
|
export PATH="${PATH}${PATH:+:}/app/utils/gamescope/bin:/usr/lib/extensions/vulkan/MangoHud/bin"
|
||||||
|
export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}${LD_LIBRARY_PATH:+:}/usr/lib/extensions/vulkan/MangoHud/\$LIB/"
|
||||||
|
|
||||||
|
exec /app/bin/prismrun "$@"
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,40 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2022 Tayou <tayou@gmx.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
@ -7,7 +44,6 @@
|
|||||||
#include <QIcon>
|
#include <QIcon>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <updater/GoUpdate.h>
|
|
||||||
|
|
||||||
#include <BaseInstance.h>
|
#include <BaseInstance.h>
|
||||||
|
|
||||||
@ -27,12 +63,13 @@ class AccountList;
|
|||||||
class IconList;
|
class IconList;
|
||||||
class QNetworkAccessManager;
|
class QNetworkAccessManager;
|
||||||
class JavaInstallList;
|
class JavaInstallList;
|
||||||
class UpdateChecker;
|
class ExternalUpdater;
|
||||||
class BaseProfilerFactory;
|
class BaseProfilerFactory;
|
||||||
class BaseDetachedToolFactory;
|
class BaseDetachedToolFactory;
|
||||||
class TranslationsModel;
|
class TranslationsModel;
|
||||||
class ITheme;
|
class ITheme;
|
||||||
class MCEditTool;
|
class MCEditTool;
|
||||||
|
class ThemeManager;
|
||||||
|
|
||||||
namespace Meta {
|
namespace Meta {
|
||||||
class Index;
|
class Index;
|
||||||
@ -55,10 +92,22 @@ public:
|
|||||||
Initialized
|
Initialized
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Capability {
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
SupportsMSA = 1 << 0,
|
||||||
|
SupportsFlame = 1 << 1,
|
||||||
|
SupportsGameMode = 1 << 2,
|
||||||
|
SupportsMangoHud = 1 << 3,
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(Capabilities, Capability)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Application(int &argc, char **argv);
|
Application(int &argc, char **argv);
|
||||||
virtual ~Application();
|
virtual ~Application();
|
||||||
|
|
||||||
|
bool event(QEvent* event) override;
|
||||||
|
|
||||||
std::shared_ptr<SettingsObject> settings() const {
|
std::shared_ptr<SettingsObject> settings() const {
|
||||||
return m_settings;
|
return m_settings;
|
||||||
}
|
}
|
||||||
@ -71,14 +120,18 @@ public:
|
|||||||
|
|
||||||
void setIconTheme(const QString& name);
|
void setIconTheme(const QString& name);
|
||||||
|
|
||||||
std::vector<ITheme *> getValidApplicationThemes();
|
void applyCurrentlySelectedTheme(bool initial = false);
|
||||||
|
|
||||||
void setApplicationTheme(const QString& name, bool initial);
|
QList<ITheme*> getValidApplicationThemes();
|
||||||
|
|
||||||
shared_qobject_ptr<UpdateChecker> updateChecker() {
|
void setApplicationTheme(const QString& name);
|
||||||
return m_updateChecker;
|
|
||||||
|
shared_qobject_ptr<ExternalUpdater> updater() {
|
||||||
|
return m_updater;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void triggerUpdateCheck();
|
||||||
|
|
||||||
std::shared_ptr<TranslationsModel> translations();
|
std::shared_ptr<TranslationsModel> translations();
|
||||||
|
|
||||||
std::shared_ptr<JavaInstallList> javalist();
|
std::shared_ptr<JavaInstallList> javalist();
|
||||||
@ -115,15 +168,33 @@ public:
|
|||||||
|
|
||||||
shared_qobject_ptr<Meta::Index> metadataIndex();
|
shared_qobject_ptr<Meta::Index> metadataIndex();
|
||||||
|
|
||||||
QString getJarsPath();
|
void updateCapabilities();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Finds and returns the full path to a jar file.
|
||||||
|
* Returns a null-string if it could not be found.
|
||||||
|
*/
|
||||||
|
QString getJarPath(QString jarFile);
|
||||||
|
|
||||||
QString getMSAClientID();
|
QString getMSAClientID();
|
||||||
|
QString getFlameAPIKey();
|
||||||
|
QString getModrinthAPIToken();
|
||||||
|
QString getUserAgent();
|
||||||
|
QString getUserAgentUncached();
|
||||||
|
|
||||||
/// this is the root of the 'installation'. Used for automatic updates
|
/// this is the root of the 'installation'. Used for automatic updates
|
||||||
const QString &root() {
|
const QString &root() {
|
||||||
return m_rootPath;
|
return m_rootPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPortable() {
|
||||||
|
return m_portable;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Capabilities capabilities() {
|
||||||
|
return m_capabilities;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Opens a json file using either a system default editor, or, if not empty, the editor
|
* Opens a json file using either a system default editor, or, if not empty, the editor
|
||||||
* specified in the settings
|
* specified in the settings
|
||||||
@ -138,20 +209,29 @@ public:
|
|||||||
|
|
||||||
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
|
||||||
|
|
||||||
|
int suitableMaxMem();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void updateAllowedChanged(bool status);
|
void updateAllowedChanged(bool status);
|
||||||
void globalSettingsAboutToOpen();
|
void globalSettingsAboutToOpen();
|
||||||
void globalSettingsClosed();
|
void globalSettingsClosed();
|
||||||
|
int currentCatChanged(int index);
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
void clickedOnDock();
|
||||||
|
#endif
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
bool launch(
|
bool launch(
|
||||||
InstancePtr instance,
|
InstancePtr instance,
|
||||||
bool online = true,
|
bool online = true,
|
||||||
|
bool demo = false,
|
||||||
BaseProfilerFactory *profiler = nullptr,
|
BaseProfilerFactory *profiler = nullptr,
|
||||||
MinecraftServerTargetPtr serverToJoin = nullptr,
|
MinecraftServerTargetPtr serverToJoin = nullptr,
|
||||||
MinecraftAccountPtr accountToUse = nullptr
|
MinecraftAccountPtr accountToUse = nullptr
|
||||||
);
|
);
|
||||||
bool kill(InstancePtr instance);
|
bool kill(InstancePtr instance);
|
||||||
|
void closeCurrentWindow();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_windowClose();
|
void on_windowClose();
|
||||||
@ -161,6 +241,7 @@ private slots:
|
|||||||
void setupWizardFinished(int status);
|
void setupWizardFinished(int status);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool handleDataMigration(const QString & currentData, const QString & oldData, const QString & name, const QString & configFile) const;
|
||||||
bool createSetupWizard();
|
bool createSetupWizard();
|
||||||
void performMainStartupAction();
|
void performMainStartupAction();
|
||||||
|
|
||||||
@ -177,7 +258,7 @@ private:
|
|||||||
|
|
||||||
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
shared_qobject_ptr<QNetworkAccessManager> m_network;
|
||||||
|
|
||||||
shared_qobject_ptr<UpdateChecker> m_updateChecker;
|
shared_qobject_ptr<ExternalUpdater> m_updater;
|
||||||
shared_qobject_ptr<AccountList> m_accounts;
|
shared_qobject_ptr<AccountList> m_accounts;
|
||||||
|
|
||||||
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
shared_qobject_ptr<HttpMetaCache> m_metacache;
|
||||||
@ -189,15 +270,20 @@ private:
|
|||||||
std::shared_ptr<JavaInstallList> m_javalist;
|
std::shared_ptr<JavaInstallList> m_javalist;
|
||||||
std::shared_ptr<TranslationsModel> m_translations;
|
std::shared_ptr<TranslationsModel> m_translations;
|
||||||
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
||||||
std::map<QString, std::unique_ptr<ITheme>> m_themes;
|
|
||||||
std::unique_ptr<MCEditTool> m_mcedit;
|
std::unique_ptr<MCEditTool> m_mcedit;
|
||||||
QString m_jarsPath;
|
|
||||||
QSet<QString> m_features;
|
QSet<QString> m_features;
|
||||||
|
std::unique_ptr<ThemeManager> m_themeManager;
|
||||||
|
|
||||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||||
|
|
||||||
QString m_rootPath;
|
QString m_rootPath;
|
||||||
Status m_status = Application::StartingUp;
|
Status m_status = Application::StartingUp;
|
||||||
|
Capabilities m_capabilities;
|
||||||
|
bool m_portable = false;
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
Qt::ApplicationState m_prevAppState = Qt::ApplicationInactive;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
#if defined Q_OS_WIN32
|
||||||
// used on Windows to attach the standard IO streams
|
// used on Windows to attach the standard IO streams
|
||||||
@ -228,7 +314,7 @@ public:
|
|||||||
QString m_serverToJoin;
|
QString m_serverToJoin;
|
||||||
QString m_profileToUse;
|
QString m_profileToUse;
|
||||||
bool m_liveCheck = false;
|
bool m_liveCheck = false;
|
||||||
QUrl m_zipToImport;
|
QList<QUrl> m_zipsToImport;
|
||||||
|
QString m_instanceIdToShowWindowOf;
|
||||||
std::unique_ptr<QFile> logFile;
|
std::unique_ptr<QFile> logFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,18 +1,54 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ApplicationMessage.h"
|
#include "ApplicationMessage.h"
|
||||||
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include "Json.h"
|
||||||
|
|
||||||
void ApplicationMessage::parse(const QByteArray & input) {
|
void ApplicationMessage::parse(const QByteArray & input) {
|
||||||
auto doc = QJsonDocument::fromBinaryData(input);
|
auto doc = Json::requireDocument(input, "ApplicationMessage");
|
||||||
auto root = doc.object();
|
auto root = Json::requireObject(doc, "ApplicationMessage");
|
||||||
|
|
||||||
command = root.value("command").toString();
|
command = root.value("command").toString();
|
||||||
args.clear();
|
args.clear();
|
||||||
|
|
||||||
auto parsedArgs = root.value("args").toObject();
|
auto parsedArgs = root.value("args").toObject();
|
||||||
for(auto iter = parsedArgs.begin(); iter != parsedArgs.end(); iter++) {
|
for(auto iter = parsedArgs.constBegin(); iter != parsedArgs.constEnd(); iter++) {
|
||||||
args[iter.key()] = iter.value().toString();
|
args.insert(iter.key(), iter.value().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,12 +56,10 @@ QByteArray ApplicationMessage::serialize() {
|
|||||||
QJsonObject root;
|
QJsonObject root;
|
||||||
root.insert("command", command);
|
root.insert("command", command);
|
||||||
QJsonObject outArgs;
|
QJsonObject outArgs;
|
||||||
for (auto iter = args.begin(); iter != args.end(); iter++) {
|
for (auto iter = args.constBegin(); iter != args.constEnd(); iter++) {
|
||||||
outArgs[iter.key()] = iter.value();
|
outArgs.insert(iter.key(), iter.value());
|
||||||
}
|
}
|
||||||
root.insert("args", outArgs);
|
root.insert("args", outArgs);
|
||||||
|
|
||||||
QJsonDocument out;
|
return Json::toText(root);
|
||||||
out.setObject(root);
|
|
||||||
return out.toBinaryData();
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QMap>
|
#include <QHash>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
struct ApplicationMessage {
|
struct ApplicationMessage {
|
||||||
QString command;
|
QString command;
|
||||||
QMap<QString, QString> args;
|
QHash<QString, QString> args;
|
||||||
|
|
||||||
QByteArray serialize();
|
QByteArray serialize();
|
||||||
void parse(const QByteArray & input);
|
void parse(const QByteArray & input);
|
||||||
|
@ -17,13 +17,14 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "BaseVersion.h"
|
||||||
|
|
||||||
class MinecraftInstance;
|
class MinecraftInstance;
|
||||||
class QDir;
|
class QDir;
|
||||||
class QString;
|
class QString;
|
||||||
class QObject;
|
class QObject;
|
||||||
class Task;
|
class Task;
|
||||||
class BaseVersion;
|
class BaseVersion;
|
||||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
|
||||||
|
|
||||||
class BaseInstaller
|
class BaseInstaller
|
||||||
{
|
{
|
||||||
@ -35,7 +36,7 @@ public:
|
|||||||
virtual bool add(MinecraftInstance *to);
|
virtual bool add(MinecraftInstance *to);
|
||||||
virtual bool remove(MinecraftInstance *from);
|
virtual bool remove(MinecraftInstance *from);
|
||||||
|
|
||||||
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersionPtr version, QObject *parent) = 0;
|
virtual Task *createInstallTask(MinecraftInstance *instance, BaseVersion::Ptr version, QObject *parent) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual QString id() const = 0;
|
virtual QString id() const = 0;
|
||||||
|
@ -1,4 +1,25 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -18,6 +39,9 @@
|
|||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
#include "settings/INISettingsObject.h"
|
#include "settings/INISettingsObject.h"
|
||||||
#include "settings/Setting.h"
|
#include "settings/Setting.h"
|
||||||
@ -31,15 +55,28 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
|||||||
: QObject()
|
: QObject()
|
||||||
{
|
{
|
||||||
m_settings = settings;
|
m_settings = settings;
|
||||||
|
m_global_settings = globalSettings;
|
||||||
m_rootDir = rootDir;
|
m_rootDir = rootDir;
|
||||||
|
|
||||||
m_settings->registerSetting("name", "Unnamed Instance");
|
m_settings->registerSetting("name", "Unnamed Instance");
|
||||||
m_settings->registerSetting("iconKey", "default");
|
m_settings->registerSetting("iconKey", "default");
|
||||||
m_settings->registerSetting("notes", "");
|
m_settings->registerSetting("notes", "");
|
||||||
|
|
||||||
m_settings->registerSetting("lastLaunchTime", 0);
|
m_settings->registerSetting("lastLaunchTime", 0);
|
||||||
m_settings->registerSetting("totalTimePlayed", 0);
|
m_settings->registerSetting("totalTimePlayed", 0);
|
||||||
m_settings->registerSetting("lastTimePlayed", 0);
|
m_settings->registerSetting("lastTimePlayed", 0);
|
||||||
m_settings->registerSetting("InstanceType", "OneSix");
|
|
||||||
|
m_settings->registerSetting("linkedInstances", "[]");
|
||||||
|
|
||||||
|
// Game time override
|
||||||
|
auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false);
|
||||||
|
m_settings->registerOverride(globalSettings->getSetting("ShowGameTime"), gameTimeOverride);
|
||||||
|
m_settings->registerOverride(globalSettings->getSetting("RecordGameTime"), gameTimeOverride);
|
||||||
|
|
||||||
|
// NOTE: Sometimees InstanceType is already registered, as it was used to identify the type of
|
||||||
|
// a locally stored instance
|
||||||
|
if (!m_settings->getSetting("InstanceType"))
|
||||||
|
m_settings->registerSetting("InstanceType", "");
|
||||||
|
|
||||||
// Custom Commands
|
// Custom Commands
|
||||||
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
auto commandSetting = m_settings->registerSetting({"OverrideCommands","OverrideLaunchCmd"}, false);
|
||||||
@ -56,6 +93,14 @@ BaseInstance::BaseInstance(SettingsObjectPtr globalSettings, SettingsObjectPtr s
|
|||||||
|
|
||||||
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
|
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleMaxLines"), nullptr);
|
||||||
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
|
m_settings->registerPassthrough(globalSettings->getSetting("ConsoleOverflowStop"), nullptr);
|
||||||
|
|
||||||
|
// Managed Packs
|
||||||
|
m_settings->registerSetting("ManagedPack", false);
|
||||||
|
m_settings->registerSetting("ManagedPackType", "");
|
||||||
|
m_settings->registerSetting("ManagedPackID", "");
|
||||||
|
m_settings->registerSetting("ManagedPackName", "");
|
||||||
|
m_settings->registerSetting("ManagedPackVersionID", "");
|
||||||
|
m_settings->registerSetting("ManagedPackVersionName", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString BaseInstance::getPreLaunchCommand()
|
QString BaseInstance::getPreLaunchCommand()
|
||||||
@ -73,9 +118,59 @@ QString BaseInstance::getPostExitCommand()
|
|||||||
return settings()->get("PostExitCommand").toString();
|
return settings()->get("PostExitCommand").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BaseInstance::isManagedPack() const
|
||||||
|
{
|
||||||
|
return m_settings->get("ManagedPack").toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseInstance::getManagedPackType() const
|
||||||
|
{
|
||||||
|
return m_settings->get("ManagedPackType").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseInstance::getManagedPackID() const
|
||||||
|
{
|
||||||
|
return m_settings->get("ManagedPackID").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseInstance::getManagedPackName() const
|
||||||
|
{
|
||||||
|
return m_settings->get("ManagedPackName").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseInstance::getManagedPackVersionID() const
|
||||||
|
{
|
||||||
|
return m_settings->get("ManagedPackVersionID").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString BaseInstance::getManagedPackVersionName() const
|
||||||
|
{
|
||||||
|
return m_settings->get("ManagedPackVersionName").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseInstance::setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version)
|
||||||
|
{
|
||||||
|
m_settings->set("ManagedPack", true);
|
||||||
|
m_settings->set("ManagedPackType", type);
|
||||||
|
m_settings->set("ManagedPackID", id);
|
||||||
|
m_settings->set("ManagedPackName", name);
|
||||||
|
m_settings->set("ManagedPackVersionID", versionId);
|
||||||
|
m_settings->set("ManagedPackVersionName", version);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseInstance::copyManagedPack(BaseInstance& other)
|
||||||
|
{
|
||||||
|
m_settings->set("ManagedPack", other.isManagedPack());
|
||||||
|
m_settings->set("ManagedPackType", other.getManagedPackType());
|
||||||
|
m_settings->set("ManagedPackID", other.getManagedPackID());
|
||||||
|
m_settings->set("ManagedPackName", other.getManagedPackName());
|
||||||
|
m_settings->set("ManagedPackVersionID", other.getManagedPackVersionID());
|
||||||
|
m_settings->set("ManagedPackVersionName", other.getManagedPackVersionName());
|
||||||
|
}
|
||||||
|
|
||||||
int BaseInstance::getConsoleMaxLines() const
|
int BaseInstance::getConsoleMaxLines() const
|
||||||
{
|
{
|
||||||
auto lineSetting = settings()->getSetting("ConsoleMaxLines");
|
auto lineSetting = m_settings->getSetting("ConsoleMaxLines");
|
||||||
bool conversionOk = false;
|
bool conversionOk = false;
|
||||||
int maxLines = lineSetting->get().toInt(&conversionOk);
|
int maxLines = lineSetting->get().toInt(&conversionOk);
|
||||||
if(!conversionOk)
|
if(!conversionOk)
|
||||||
@ -88,7 +183,39 @@ int BaseInstance::getConsoleMaxLines() const
|
|||||||
|
|
||||||
bool BaseInstance::shouldStopOnConsoleOverflow() const
|
bool BaseInstance::shouldStopOnConsoleOverflow() const
|
||||||
{
|
{
|
||||||
return settings()->get("ConsoleOverflowStop").toBool();
|
return m_settings->get("ConsoleOverflowStop").toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList BaseInstance::getLinkedInstances() const
|
||||||
|
{
|
||||||
|
return m_settings->get("linkedInstances").toStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseInstance::setLinkedInstances(const QStringList& list)
|
||||||
|
{
|
||||||
|
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
||||||
|
m_settings->set("linkedInstances", list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BaseInstance::addLinkedInstanceId(const QString& id)
|
||||||
|
{
|
||||||
|
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
||||||
|
linkedInstances.append(id);
|
||||||
|
setLinkedInstances(linkedInstances);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseInstance::removeLinkedInstanceId(const QString& id)
|
||||||
|
{
|
||||||
|
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
||||||
|
int numRemoved = linkedInstances.removeAll(id);
|
||||||
|
setLinkedInstances(linkedInstances);
|
||||||
|
return numRemoved > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BaseInstance::isLinkedToInstanceId(const QString& id) const
|
||||||
|
{
|
||||||
|
auto linkedInstances = m_settings->get("linkedInstances").toStringList();
|
||||||
|
return linkedInstances.contains(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::iconUpdated(QString key)
|
void BaseInstance::iconUpdated(QString key)
|
||||||
@ -163,7 +290,7 @@ void BaseInstance::setRunning(bool running)
|
|||||||
|
|
||||||
int64_t BaseInstance::totalTimePlayed() const
|
int64_t BaseInstance::totalTimePlayed() const
|
||||||
{
|
{
|
||||||
qint64 current = settings()->get("totalTimePlayed").toLongLong();
|
qint64 current = m_settings->get("totalTimePlayed").toLongLong();
|
||||||
if(m_isRunning)
|
if(m_isRunning)
|
||||||
{
|
{
|
||||||
QDateTime timeNow = QDateTime::currentDateTime();
|
QDateTime timeNow = QDateTime::currentDateTime();
|
||||||
@ -179,7 +306,7 @@ int64_t BaseInstance::lastTimePlayed() const
|
|||||||
QDateTime timeNow = QDateTime::currentDateTime();
|
QDateTime timeNow = QDateTime::currentDateTime();
|
||||||
return m_timeStarted.secsTo(timeNow);
|
return m_timeStarted.secsTo(timeNow);
|
||||||
}
|
}
|
||||||
return settings()->get("lastTimePlayed").toLongLong();
|
return m_settings->get("lastTimePlayed").toLongLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseInstance::resetTimePlayed()
|
void BaseInstance::resetTimePlayed()
|
||||||
@ -198,8 +325,10 @@ QString BaseInstance::instanceRoot() const
|
|||||||
return m_rootDir;
|
return m_rootDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsObjectPtr BaseInstance::settings() const
|
SettingsObjectPtr BaseInstance::settings()
|
||||||
{
|
{
|
||||||
|
loadSpecificSettings();
|
||||||
|
|
||||||
return m_settings;
|
return m_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,11 +391,11 @@ QString BaseInstance::name() const
|
|||||||
|
|
||||||
QString BaseInstance::windowTitle() const
|
QString BaseInstance::windowTitle() const
|
||||||
{
|
{
|
||||||
return BuildConfig.LAUNCHER_NAME + ": " + name().replace(QRegExp("[ \n\r\t]+"), " ");
|
return BuildConfig.LAUNCHER_DISPLAYNAME + ": " + name().replace(QRegularExpression("\\s+"), " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: why is this here? move it to MinecraftInstance!!!
|
// FIXME: why is this here? move it to MinecraftInstance!!!
|
||||||
QStringList BaseInstance::extraArguments() const
|
QStringList BaseInstance::extraArguments()
|
||||||
{
|
{
|
||||||
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
|
||||||
}
|
}
|
||||||
@ -275,3 +404,8 @@ shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
|
|||||||
{
|
{
|
||||||
return m_launchProcess;
|
return m_launchProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseInstance::updateRuntimeContext()
|
||||||
|
{
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
@ -1,4 +1,25 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2022 Jamie Mansfield <jmansfield@cadixdev.org>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -33,6 +54,7 @@
|
|||||||
#include "net/Mode.h"
|
#include "net/Mode.h"
|
||||||
|
|
||||||
#include "minecraft/launch/MinecraftServerTarget.h"
|
#include "minecraft/launch/MinecraftServerTarget.h"
|
||||||
|
#include "RuntimeContext.h"
|
||||||
|
|
||||||
class QDir;
|
class QDir;
|
||||||
class Task;
|
class Task;
|
||||||
@ -119,13 +141,22 @@ public:
|
|||||||
QString getPostExitCommand();
|
QString getPostExitCommand();
|
||||||
QString getWrapperCommand();
|
QString getWrapperCommand();
|
||||||
|
|
||||||
|
bool isManagedPack() const;
|
||||||
|
QString getManagedPackType() const;
|
||||||
|
QString getManagedPackID() const;
|
||||||
|
QString getManagedPackName() const;
|
||||||
|
QString getManagedPackVersionID() const;
|
||||||
|
QString getManagedPackVersionName() const;
|
||||||
|
void setManagedPack(const QString& type, const QString& id, const QString& name, const QString& versionId, const QString& version);
|
||||||
|
void copyManagedPack(BaseInstance& other);
|
||||||
|
|
||||||
/// guess log level from a line of game log
|
/// guess log level from a line of game log
|
||||||
virtual MessageLevel::Enum guessLevel(const QString &line, MessageLevel::Enum level)
|
virtual MessageLevel::Enum guessLevel([[maybe_unused]] const QString &line, MessageLevel::Enum level)
|
||||||
{
|
{
|
||||||
return level;
|
return level;
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual QStringList extraArguments() const;
|
virtual QStringList extraArguments();
|
||||||
|
|
||||||
/// Traits. Normally inside the version, depends on instance implementation.
|
/// Traits. Normally inside the version, depends on instance implementation.
|
||||||
virtual QSet <QString> traits() const = 0;
|
virtual QSet <QString> traits() const = 0;
|
||||||
@ -141,9 +172,18 @@ public:
|
|||||||
/*!
|
/*!
|
||||||
* \brief Gets this instance's settings object.
|
* \brief Gets this instance's settings object.
|
||||||
* This settings object stores instance-specific settings.
|
* This settings object stores instance-specific settings.
|
||||||
|
*
|
||||||
|
* Note that this method is not const.
|
||||||
|
* It may call loadSpecificSettings() to ensure those are loaded.
|
||||||
|
*
|
||||||
* \return A pointer to this instance's settings object.
|
* \return A pointer to this instance's settings object.
|
||||||
*/
|
*/
|
||||||
virtual SettingsObjectPtr settings() const;
|
virtual SettingsObjectPtr settings();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Loads settings specific to an instance type if they're not already loaded.
|
||||||
|
*/
|
||||||
|
virtual void loadSpecificSettings() = 0;
|
||||||
|
|
||||||
/// returns a valid update task
|
/// returns a valid update task
|
||||||
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
virtual Task::Ptr createUpdateTask(Net::Mode mode) = 0;
|
||||||
@ -159,6 +199,7 @@ public:
|
|||||||
* Create envrironment variables for running the instance
|
* Create envrironment variables for running the instance
|
||||||
*/
|
*/
|
||||||
virtual QProcessEnvironment createEnvironment() = 0;
|
virtual QProcessEnvironment createEnvironment() = 0;
|
||||||
|
virtual QProcessEnvironment createLaunchEnvironment() = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
|
* Returns a matcher that can maps relative paths within the instance to whether they are 'log files'
|
||||||
@ -176,10 +217,16 @@ public:
|
|||||||
virtual QString instanceConfigFolder() const = 0;
|
virtual QString instanceConfigFolder() const = 0;
|
||||||
|
|
||||||
/// get variables this instance exports
|
/// get variables this instance exports
|
||||||
virtual QMap<QString, QString> getVariables() const = 0;
|
virtual QMap<QString, QString> getVariables() = 0;
|
||||||
|
|
||||||
virtual QString typeName() const = 0;
|
virtual QString typeName() const = 0;
|
||||||
|
|
||||||
|
void updateRuntimeContext();
|
||||||
|
RuntimeContext runtimeContext() const
|
||||||
|
{
|
||||||
|
return m_runtimeContext;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasVersionBroken() const
|
bool hasVersionBroken() const
|
||||||
{
|
{
|
||||||
return m_hasBrokenVersion;
|
return m_hasBrokenVersion;
|
||||||
@ -235,9 +282,20 @@ public:
|
|||||||
int getConsoleMaxLines() const;
|
int getConsoleMaxLines() const;
|
||||||
bool shouldStopOnConsoleOverflow() const;
|
bool shouldStopOnConsoleOverflow() const;
|
||||||
|
|
||||||
|
QStringList getLinkedInstances() const;
|
||||||
|
void setLinkedInstances(const QStringList& list);
|
||||||
|
void addLinkedInstanceId(const QString& id);
|
||||||
|
bool removeLinkedInstanceId(const QString& id);
|
||||||
|
bool isLinkedToInstanceId(const QString& id) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void changeStatus(Status newStatus);
|
void changeStatus(Status newStatus);
|
||||||
|
|
||||||
|
SettingsObjectPtr globalSettings() const { return m_global_settings.lock(); };
|
||||||
|
|
||||||
|
bool isSpecificSettingsLoaded() const { return m_specific_settings_loaded; }
|
||||||
|
void setSpecificSettingsLoaded(bool loaded) { m_specific_settings_loaded = loaded; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/*!
|
/*!
|
||||||
* \brief Signal emitted when properties relevant to the instance view change
|
* \brief Signal emitted when properties relevant to the instance view change
|
||||||
@ -260,12 +318,17 @@ protected: /* data */
|
|||||||
bool m_isRunning = false;
|
bool m_isRunning = false;
|
||||||
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
shared_qobject_ptr<LaunchTask> m_launchProcess;
|
||||||
QDateTime m_timeStarted;
|
QDateTime m_timeStarted;
|
||||||
|
RuntimeContext m_runtimeContext;
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
Status m_status = Status::Present;
|
Status m_status = Status::Present;
|
||||||
bool m_crashed = false;
|
bool m_crashed = false;
|
||||||
bool m_hasUpdate = false;
|
bool m_hasUpdate = false;
|
||||||
bool m_hasBrokenVersion = false;
|
bool m_hasBrokenVersion = false;
|
||||||
|
|
||||||
|
SettingsObjectWeakPtr m_global_settings;
|
||||||
|
bool m_specific_settings_loaded = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
class BaseVersion
|
class BaseVersion
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using Ptr = std::shared_ptr<BaseVersion>;
|
||||||
virtual ~BaseVersion() {}
|
virtual ~BaseVersion() {}
|
||||||
/*!
|
/*!
|
||||||
* A string used to identify this version in config files.
|
* A string used to identify this version in config files.
|
||||||
@ -54,6 +55,4 @@ public:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<BaseVersion> BaseVersionPtr;
|
Q_DECLARE_METATYPE(BaseVersion::Ptr)
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(BaseVersionPtr)
|
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -20,20 +40,20 @@ BaseVersionList::BaseVersionList(QObject *parent) : QAbstractListModel(parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseVersionPtr BaseVersionList::findVersion(const QString &descriptor)
|
BaseVersion::Ptr BaseVersionList::findVersion(const QString &descriptor)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count(); i++)
|
for (int i = 0; i < count(); i++)
|
||||||
{
|
{
|
||||||
if (at(i)->descriptor() == descriptor)
|
if (at(i)->descriptor() == descriptor)
|
||||||
return at(i);
|
return at(i);
|
||||||
}
|
}
|
||||||
return BaseVersionPtr();
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseVersionPtr BaseVersionList::getRecommended() const
|
BaseVersion::Ptr BaseVersionList::getRecommended() const
|
||||||
{
|
{
|
||||||
if (count() <= 0)
|
if (count() <= 0)
|
||||||
return BaseVersionPtr();
|
return nullptr;
|
||||||
else
|
else
|
||||||
return at(0);
|
return at(0);
|
||||||
}
|
}
|
||||||
@ -46,12 +66,12 @@ QVariant BaseVersionList::data(const QModelIndex &index, int role) const
|
|||||||
if (index.row() > count())
|
if (index.row() > count())
|
||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
BaseVersionPtr version = at(index.row());
|
BaseVersion::Ptr version = at(index.row());
|
||||||
|
|
||||||
switch (role)
|
switch (role)
|
||||||
{
|
{
|
||||||
case VersionPointerRole:
|
case VersionPointerRole:
|
||||||
return qVariantFromValue(version);
|
return QVariant::fromValue(version);
|
||||||
|
|
||||||
case VersionRole:
|
case VersionRole:
|
||||||
return version->name();
|
return version->name();
|
||||||
@ -75,12 +95,12 @@ BaseVersionList::RoleList BaseVersionList::providesRoles() const
|
|||||||
int BaseVersionList::rowCount(const QModelIndex &parent) const
|
int BaseVersionList::rowCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
// Return count
|
// Return count
|
||||||
return count();
|
return parent.isValid() ? 0 : count();
|
||||||
}
|
}
|
||||||
|
|
||||||
int BaseVersionList::columnCount(const QModelIndex &parent) const
|
int BaseVersionList::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return 1;
|
return parent.isValid() ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> BaseVersionList::roleNames() const
|
QHash<int, QByteArray> BaseVersionList::roleNames() const
|
||||||
|
@ -70,7 +70,7 @@ public:
|
|||||||
virtual bool isLoaded() = 0;
|
virtual bool isLoaded() = 0;
|
||||||
|
|
||||||
//! Gets the version at the given index.
|
//! Gets the version at the given index.
|
||||||
virtual const BaseVersionPtr at(int i) const = 0;
|
virtual const BaseVersion::Ptr at(int i) const = 0;
|
||||||
|
|
||||||
//! Returns the number of versions in the list.
|
//! Returns the number of versions in the list.
|
||||||
virtual int count() const = 0;
|
virtual int count() const = 0;
|
||||||
@ -90,13 +90,13 @@ public:
|
|||||||
* \return A const pointer to the version with the given descriptor. NULL if
|
* \return A const pointer to the version with the given descriptor. NULL if
|
||||||
* one doesn't exist.
|
* one doesn't exist.
|
||||||
*/
|
*/
|
||||||
virtual BaseVersionPtr findVersion(const QString &descriptor);
|
virtual BaseVersion::Ptr findVersion(const QString &descriptor);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Gets the recommended version from this list
|
* \brief Gets the recommended version from this list
|
||||||
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
* If the list doesn't support recommended versions, this works exactly as getLatestStable
|
||||||
*/
|
*/
|
||||||
virtual BaseVersionPtr getRecommended() const;
|
virtual BaseVersion::Ptr getRecommended() const;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Sorts the version list.
|
* Sorts the version list.
|
||||||
@ -117,5 +117,5 @@ slots:
|
|||||||
* then copies the versions and sets their parents correctly.
|
* then copies the versions and sets their parents correctly.
|
||||||
* \param versions List of versions whose parents should be set.
|
* \param versions List of versions whose parents should be set.
|
||||||
*/
|
*/
|
||||||
virtual void updateListData(QList<BaseVersionPtr> versions) = 0;
|
virtual void updateListData(QList<BaseVersion::Ptr> versions) = 0;
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
|
||||||
*
|
*
|
||||||
@ -47,7 +67,7 @@ QStringList splitArgs(QString args)
|
|||||||
if (cchar == '\\')
|
if (cchar == '\\')
|
||||||
escape = true;
|
escape = true;
|
||||||
else if (cchar == inquotes)
|
else if (cchar == inquotes)
|
||||||
inquotes = 0;
|
inquotes = QChar::Null;
|
||||||
else
|
else
|
||||||
current += cchar;
|
current += cchar;
|
||||||
// otherwise
|
// otherwise
|
||||||
@ -72,412 +92,4 @@ QStringList splitArgs(QString args)
|
|||||||
argv << current;
|
argv << current;
|
||||||
return argv;
|
return argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser::Parser(FlagStyle::Enum flagStyle, ArgumentStyle::Enum argStyle)
|
|
||||||
{
|
|
||||||
m_flagStyle = flagStyle;
|
|
||||||
m_argStyle = argStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// styles setter/getter
|
|
||||||
void Parser::setArgumentStyle(ArgumentStyle::Enum style)
|
|
||||||
{
|
|
||||||
m_argStyle = style;
|
|
||||||
}
|
|
||||||
ArgumentStyle::Enum Parser::argumentStyle()
|
|
||||||
{
|
|
||||||
return m_argStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::setFlagStyle(FlagStyle::Enum style)
|
|
||||||
{
|
|
||||||
m_flagStyle = style;
|
|
||||||
}
|
|
||||||
FlagStyle::Enum Parser::flagStyle()
|
|
||||||
{
|
|
||||||
return m_flagStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup methods
|
|
||||||
void Parser::addSwitch(QString name, bool def)
|
|
||||||
{
|
|
||||||
if (m_params.contains(name))
|
|
||||||
throw "Name not unique";
|
|
||||||
|
|
||||||
OptionDef *param = new OptionDef;
|
|
||||||
param->type = otSwitch;
|
|
||||||
param->name = name;
|
|
||||||
param->metavar = QString("<%1>").arg(name);
|
|
||||||
param->def = def;
|
|
||||||
|
|
||||||
m_options[name] = param;
|
|
||||||
m_params[name] = (CommonDef *)param;
|
|
||||||
m_optionList.append(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::addOption(QString name, QVariant def)
|
|
||||||
{
|
|
||||||
if (m_params.contains(name))
|
|
||||||
throw "Name not unique";
|
|
||||||
|
|
||||||
OptionDef *param = new OptionDef;
|
|
||||||
param->type = otOption;
|
|
||||||
param->name = name;
|
|
||||||
param->metavar = QString("<%1>").arg(name);
|
|
||||||
param->def = def;
|
|
||||||
|
|
||||||
m_options[name] = param;
|
|
||||||
m_params[name] = (CommonDef *)param;
|
|
||||||
m_optionList.append(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::addArgument(QString name, bool required, QVariant def)
|
|
||||||
{
|
|
||||||
if (m_params.contains(name))
|
|
||||||
throw "Name not unique";
|
|
||||||
|
|
||||||
PositionalDef *param = new PositionalDef;
|
|
||||||
param->name = name;
|
|
||||||
param->def = def;
|
|
||||||
param->required = required;
|
|
||||||
param->metavar = name;
|
|
||||||
|
|
||||||
m_positionals.append(param);
|
|
||||||
m_params[name] = (CommonDef *)param;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::addDocumentation(QString name, QString doc, QString metavar)
|
|
||||||
{
|
|
||||||
if (!m_params.contains(name))
|
|
||||||
throw "Name does not exist";
|
|
||||||
|
|
||||||
CommonDef *param = m_params[name];
|
|
||||||
param->doc = doc;
|
|
||||||
if (!metavar.isNull())
|
|
||||||
param->metavar = metavar;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::addShortOpt(QString name, QChar flag)
|
|
||||||
{
|
|
||||||
if (!m_params.contains(name))
|
|
||||||
throw "Name does not exist";
|
|
||||||
if (!m_options.contains(name))
|
|
||||||
throw "Name is not an Option or Swtich";
|
|
||||||
|
|
||||||
OptionDef *param = m_options[name];
|
|
||||||
m_flags[flag] = param;
|
|
||||||
param->flag = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
// help methods
|
|
||||||
QString Parser::compileHelp(QString progName, int helpIndent, bool useFlags)
|
|
||||||
{
|
|
||||||
QStringList help;
|
|
||||||
help << compileUsage(progName, useFlags) << "\r\n";
|
|
||||||
|
|
||||||
// positionals
|
|
||||||
if (!m_positionals.isEmpty())
|
|
||||||
{
|
|
||||||
help << "\r\n";
|
|
||||||
help << "Positional arguments:\r\n";
|
|
||||||
QListIterator<PositionalDef *> it2(m_positionals);
|
|
||||||
while (it2.hasNext())
|
|
||||||
{
|
|
||||||
PositionalDef *param = it2.next();
|
|
||||||
help << " " << param->metavar;
|
|
||||||
help << " " << QString(helpIndent - param->metavar.length() - 1, ' ');
|
|
||||||
help << param->doc << "\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options
|
|
||||||
if (!m_optionList.isEmpty())
|
|
||||||
{
|
|
||||||
help << "\r\n";
|
|
||||||
QString optPrefix, flagPrefix;
|
|
||||||
getPrefix(optPrefix, flagPrefix);
|
|
||||||
|
|
||||||
help << "Options & Switches:\r\n";
|
|
||||||
QListIterator<OptionDef *> it(m_optionList);
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
OptionDef *option = it.next();
|
|
||||||
help << " ";
|
|
||||||
int nameLength = optPrefix.length() + option->name.length();
|
|
||||||
if (!option->flag.isNull())
|
|
||||||
{
|
|
||||||
nameLength += 3 + flagPrefix.length();
|
|
||||||
help << flagPrefix << option->flag << ", ";
|
|
||||||
}
|
|
||||||
help << optPrefix << option->name;
|
|
||||||
if (option->type == otOption)
|
|
||||||
{
|
|
||||||
QString arg = QString("%1%2").arg(
|
|
||||||
((m_argStyle == ArgumentStyle::Equals) ? "=" : " "), option->metavar);
|
|
||||||
nameLength += arg.length();
|
|
||||||
help << arg;
|
|
||||||
}
|
|
||||||
help << " " << QString(helpIndent - nameLength - 1, ' ');
|
|
||||||
help << option->doc << "\r\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return help.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Parser::compileUsage(QString progName, bool useFlags)
|
|
||||||
{
|
|
||||||
QStringList usage;
|
|
||||||
usage << "Usage: " << progName;
|
|
||||||
|
|
||||||
QString optPrefix, flagPrefix;
|
|
||||||
getPrefix(optPrefix, flagPrefix);
|
|
||||||
|
|
||||||
// options
|
|
||||||
QListIterator<OptionDef *> it(m_optionList);
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
OptionDef *option = it.next();
|
|
||||||
usage << " [";
|
|
||||||
if (!option->flag.isNull() && useFlags)
|
|
||||||
usage << flagPrefix << option->flag;
|
|
||||||
else
|
|
||||||
usage << optPrefix << option->name;
|
|
||||||
if (option->type == otOption)
|
|
||||||
usage << ((m_argStyle == ArgumentStyle::Equals) ? "=" : " ") << option->metavar;
|
|
||||||
usage << "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
// arguments
|
|
||||||
QListIterator<PositionalDef *> it2(m_positionals);
|
|
||||||
while (it2.hasNext())
|
|
||||||
{
|
|
||||||
PositionalDef *param = it2.next();
|
|
||||||
usage << " " << (param->required ? "<" : "[");
|
|
||||||
usage << param->metavar;
|
|
||||||
usage << (param->required ? ">" : "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
return usage.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsing
|
|
||||||
QHash<QString, QVariant> Parser::parse(QStringList argv)
|
|
||||||
{
|
|
||||||
QHash<QString, QVariant> map;
|
|
||||||
|
|
||||||
QStringListIterator it(argv);
|
|
||||||
QString programName = it.next();
|
|
||||||
|
|
||||||
QString optionPrefix;
|
|
||||||
QString flagPrefix;
|
|
||||||
QListIterator<PositionalDef *> positionals(m_positionals);
|
|
||||||
QStringList expecting;
|
|
||||||
|
|
||||||
getPrefix(optionPrefix, flagPrefix);
|
|
||||||
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
QString arg = it.next();
|
|
||||||
|
|
||||||
if (!expecting.isEmpty())
|
|
||||||
// we were expecting an argument
|
|
||||||
{
|
|
||||||
QString name = expecting.first();
|
|
||||||
/*
|
|
||||||
if (map.contains(name))
|
|
||||||
throw ParsingError(
|
|
||||||
QString("Option %2%1 was given multiple times").arg(name, optionPrefix));
|
|
||||||
*/
|
|
||||||
map[name] = QVariant(arg);
|
|
||||||
|
|
||||||
expecting.removeFirst();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.startsWith(optionPrefix))
|
|
||||||
// we have an option
|
|
||||||
{
|
|
||||||
// qDebug("Found option %s", qPrintable(arg));
|
|
||||||
|
|
||||||
QString name = arg.mid(optionPrefix.length());
|
|
||||||
QString equals;
|
|
||||||
|
|
||||||
if ((m_argStyle == ArgumentStyle::Equals ||
|
|
||||||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
|
|
||||||
name.contains("="))
|
|
||||||
{
|
|
||||||
int i = name.indexOf("=");
|
|
||||||
equals = name.mid(i + 1);
|
|
||||||
name = name.left(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_options.contains(name))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
if (map.contains(name))
|
|
||||||
throw ParsingError(QString("Option %2%1 was given multiple times")
|
|
||||||
.arg(name, optionPrefix));
|
|
||||||
*/
|
|
||||||
OptionDef *option = m_options[name];
|
|
||||||
if (option->type == otSwitch)
|
|
||||||
map[name] = true;
|
|
||||||
else // if (option->type == otOption)
|
|
||||||
{
|
|
||||||
if (m_argStyle == ArgumentStyle::Space)
|
|
||||||
expecting.append(name);
|
|
||||||
else if (!equals.isNull())
|
|
||||||
map[name] = equals;
|
|
||||||
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
|
|
||||||
expecting.append(name);
|
|
||||||
else
|
|
||||||
throw ParsingError(QString("Option %2%1 reqires an argument.")
|
|
||||||
.arg(name, optionPrefix));
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw ParsingError(QString("Unknown Option %2%1").arg(name, optionPrefix));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.startsWith(flagPrefix))
|
|
||||||
// we have (a) flag(s)
|
|
||||||
{
|
|
||||||
// qDebug("Found flags %s", qPrintable(arg));
|
|
||||||
|
|
||||||
QString flags = arg.mid(flagPrefix.length());
|
|
||||||
QString equals;
|
|
||||||
|
|
||||||
if ((m_argStyle == ArgumentStyle::Equals ||
|
|
||||||
m_argStyle == ArgumentStyle::SpaceAndEquals) &&
|
|
||||||
flags.contains("="))
|
|
||||||
{
|
|
||||||
int i = flags.indexOf("=");
|
|
||||||
equals = flags.mid(i + 1);
|
|
||||||
flags = flags.left(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < flags.length(); i++)
|
|
||||||
{
|
|
||||||
QChar flag = flags.at(i);
|
|
||||||
|
|
||||||
if (!m_flags.contains(flag))
|
|
||||||
throw ParsingError(QString("Unknown flag %2%1").arg(flag, flagPrefix));
|
|
||||||
|
|
||||||
OptionDef *option = m_flags[flag];
|
|
||||||
/*
|
|
||||||
if (map.contains(option->name))
|
|
||||||
throw ParsingError(QString("Option %2%1 was given multiple times")
|
|
||||||
.arg(option->name, optionPrefix));
|
|
||||||
*/
|
|
||||||
if (option->type == otSwitch)
|
|
||||||
map[option->name] = true;
|
|
||||||
else // if (option->type == otOption)
|
|
||||||
{
|
|
||||||
if (m_argStyle == ArgumentStyle::Space)
|
|
||||||
expecting.append(option->name);
|
|
||||||
else if (!equals.isNull())
|
|
||||||
if (i == flags.length() - 1)
|
|
||||||
map[option->name] = equals;
|
|
||||||
else
|
|
||||||
throw ParsingError(QString("Flag %4%2 of Argument-requiring Option "
|
|
||||||
"%1 not last flag in %4%3")
|
|
||||||
.arg(option->name, flag, flags, flagPrefix));
|
|
||||||
else if (m_argStyle == ArgumentStyle::SpaceAndEquals)
|
|
||||||
expecting.append(option->name);
|
|
||||||
else
|
|
||||||
throw ParsingError(QString("Option %1 reqires an argument. (flag %3%2)")
|
|
||||||
.arg(option->name, flag, flagPrefix));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// must be a positional argument
|
|
||||||
if (!positionals.hasNext())
|
|
||||||
throw ParsingError(QString("Don't know what to do with '%1'").arg(arg));
|
|
||||||
|
|
||||||
PositionalDef *param = positionals.next();
|
|
||||||
|
|
||||||
map[param->name] = arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if we're missing something
|
|
||||||
if (!expecting.isEmpty())
|
|
||||||
throw ParsingError(QString("Was still expecting arguments for %2%1").arg(
|
|
||||||
expecting.join(QString(", ") + optionPrefix), optionPrefix));
|
|
||||||
|
|
||||||
while (positionals.hasNext())
|
|
||||||
{
|
|
||||||
PositionalDef *param = positionals.next();
|
|
||||||
if (param->required)
|
|
||||||
throw ParsingError(
|
|
||||||
QString("Missing required positional argument '%1'").arg(param->name));
|
|
||||||
else
|
|
||||||
map[param->name] = param->def;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill out gaps
|
|
||||||
QListIterator<OptionDef *> iter(m_optionList);
|
|
||||||
while (iter.hasNext())
|
|
||||||
{
|
|
||||||
OptionDef *option = iter.next();
|
|
||||||
if (!map.contains(option->name))
|
|
||||||
map[option->name] = option->def;
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear defs
|
|
||||||
void Parser::clear()
|
|
||||||
{
|
|
||||||
m_flags.clear();
|
|
||||||
m_params.clear();
|
|
||||||
m_options.clear();
|
|
||||||
|
|
||||||
QMutableListIterator<OptionDef *> it(m_optionList);
|
|
||||||
while (it.hasNext())
|
|
||||||
{
|
|
||||||
OptionDef *option = it.next();
|
|
||||||
it.remove();
|
|
||||||
delete option;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMutableListIterator<PositionalDef *> it2(m_positionals);
|
|
||||||
while (it2.hasNext())
|
|
||||||
{
|
|
||||||
PositionalDef *arg = it2.next();
|
|
||||||
it2.remove();
|
|
||||||
delete arg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
Parser::~Parser()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPrefix
|
|
||||||
void Parser::getPrefix(QString &opt, QString &flag)
|
|
||||||
{
|
|
||||||
if (m_flagStyle == FlagStyle::Windows)
|
|
||||||
opt = flag = "/";
|
|
||||||
else if (m_flagStyle == FlagStyle::Unix)
|
|
||||||
opt = flag = "-";
|
|
||||||
// else if (m_flagStyle == FlagStyle::GNU)
|
|
||||||
else
|
|
||||||
{
|
|
||||||
opt = "--";
|
|
||||||
flag = "-";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParsingError
|
|
||||||
ParsingError::ParsingError(const QString &what) : std::runtime_error(what.toStdString())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -17,12 +17,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVariant>
|
|
||||||
#include <QHash>
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,212 +34,4 @@ namespace Commandline
|
|||||||
* @return a QStringList containing all arguments
|
* @return a QStringList containing all arguments
|
||||||
*/
|
*/
|
||||||
QStringList splitArgs(QString args);
|
QStringList splitArgs(QString args);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The FlagStyle enum
|
|
||||||
* Specifies how flags are decorated
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace FlagStyle
|
|
||||||
{
|
|
||||||
enum Enum
|
|
||||||
{
|
|
||||||
GNU, /**< --option and -o (GNU Style) */
|
|
||||||
Unix, /**< -option and -o (Unix Style) */
|
|
||||||
Windows, /**< /option and /o (Windows Style) */
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
Default = Windows
|
|
||||||
#else
|
|
||||||
Default = GNU
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The ArgumentStyle enum
|
|
||||||
*/
|
|
||||||
namespace ArgumentStyle
|
|
||||||
{
|
|
||||||
enum Enum
|
|
||||||
{
|
|
||||||
Space, /**< --option value */
|
|
||||||
Equals, /**< --option=value */
|
|
||||||
SpaceAndEquals, /**< --option[= ]value */
|
|
||||||
#ifdef Q_OS_WIN32
|
|
||||||
Default = Equals
|
|
||||||
#else
|
|
||||||
Default = SpaceAndEquals
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The ParsingError class
|
|
||||||
*/
|
|
||||||
class ParsingError : public std::runtime_error
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ParsingError(const QString &what);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The Parser class
|
|
||||||
*/
|
|
||||||
class Parser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Parser constructor
|
|
||||||
* @param flagStyle the FlagStyle to use in this Parser
|
|
||||||
* @param argStyle the ArgumentStyle to use in this Parser
|
|
||||||
*/
|
|
||||||
Parser(FlagStyle::Enum flagStyle = FlagStyle::Default,
|
|
||||||
ArgumentStyle::Enum argStyle = ArgumentStyle::Default);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief set the flag style
|
|
||||||
* @param style
|
|
||||||
*/
|
|
||||||
void setFlagStyle(FlagStyle::Enum style);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief get the flag style
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
FlagStyle::Enum flagStyle();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief set the argument style
|
|
||||||
* @param style
|
|
||||||
*/
|
|
||||||
void setArgumentStyle(ArgumentStyle::Enum style);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief get the argument style
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
ArgumentStyle::Enum argumentStyle();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief define a boolean switch
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param def the default value
|
|
||||||
*/
|
|
||||||
void addSwitch(QString name, bool def = false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief define an option that takes an additional argument
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param def the default value
|
|
||||||
*/
|
|
||||||
void addOption(QString name, QVariant def = QVariant());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief define a positional argument
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param required wether this argument is required
|
|
||||||
* @param def the default value
|
|
||||||
*/
|
|
||||||
void addArgument(QString name, bool required = true, QVariant def = QVariant());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief adds a flag to an existing parameter
|
|
||||||
* @param name the (existing) parameter name
|
|
||||||
* @param flag the flag character
|
|
||||||
* @see addSwitch addArgument addOption
|
|
||||||
* Note: any one parameter can only have one flag
|
|
||||||
*/
|
|
||||||
void addShortOpt(QString name, QChar flag);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief adds documentation to a Parameter
|
|
||||||
* @param name the parameter name
|
|
||||||
* @param metavar a string to be displayed as placeholder for the value
|
|
||||||
* @param doc a QString containing the documentation
|
|
||||||
* Note: on positional arguments, metavar replaces the name as displayed.
|
|
||||||
* on options , metavar replaces the value placeholder
|
|
||||||
*/
|
|
||||||
void addDocumentation(QString name, QString doc, QString metavar = QString());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief generate a help message
|
|
||||||
* @param progName the program name to use in the help message
|
|
||||||
* @param helpIndent how much the parameter documentation should be indented
|
|
||||||
* @param flagsInUsage whether we should use flags instead of options in the usage
|
|
||||||
* @return a help message
|
|
||||||
*/
|
|
||||||
QString compileHelp(QString progName, int helpIndent = 22, bool flagsInUsage = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief generate a short usage message
|
|
||||||
* @param progName the program name to use in the usage message
|
|
||||||
* @param useFlags whether we should use flags instead of options
|
|
||||||
* @return a usage message
|
|
||||||
*/
|
|
||||||
QString compileUsage(QString progName, bool useFlags = true);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief parse
|
|
||||||
* @param argv a QStringList containing the program ARGV
|
|
||||||
* @return a QHash mapping argument names to their values
|
|
||||||
*/
|
|
||||||
QHash<QString, QVariant> parse(QStringList argv);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief clear all definitions
|
|
||||||
*/
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
~Parser();
|
|
||||||
|
|
||||||
private:
|
|
||||||
FlagStyle::Enum m_flagStyle;
|
|
||||||
ArgumentStyle::Enum m_argStyle;
|
|
||||||
|
|
||||||
enum OptionType
|
|
||||||
{
|
|
||||||
otSwitch,
|
|
||||||
otOption
|
|
||||||
};
|
|
||||||
|
|
||||||
// Important: the common part MUST BE COMMON ON ALL THREE structs
|
|
||||||
struct CommonDef
|
|
||||||
{
|
|
||||||
QString name;
|
|
||||||
QString doc;
|
|
||||||
QString metavar;
|
|
||||||
QVariant def;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct OptionDef
|
|
||||||
{
|
|
||||||
// common
|
|
||||||
QString name;
|
|
||||||
QString doc;
|
|
||||||
QString metavar;
|
|
||||||
QVariant def;
|
|
||||||
// option
|
|
||||||
OptionType type;
|
|
||||||
QChar flag;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PositionalDef
|
|
||||||
{
|
|
||||||
// common
|
|
||||||
QString name;
|
|
||||||
QString doc;
|
|
||||||
QString metavar;
|
|
||||||
QVariant def;
|
|
||||||
// positional
|
|
||||||
bool required;
|
|
||||||
};
|
|
||||||
|
|
||||||
QHash<QString, OptionDef *> m_options;
|
|
||||||
QHash<QChar, OptionDef *> m_flags;
|
|
||||||
QHash<QString, CommonDef *> m_params;
|
|
||||||
QList<PositionalDef *> m_positionals;
|
|
||||||
QList<OptionDef *> m_optionList;
|
|
||||||
|
|
||||||
void getPrefix(QString &opt, QString &flag);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
96
launcher/DataMigrationTask.cpp
Normal file
96
launcher/DataMigrationTask.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
#include "DataMigrationTask.h"
|
||||||
|
|
||||||
|
#include "FileSystem.h"
|
||||||
|
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMap>
|
||||||
|
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
DataMigrationTask::DataMigrationTask(QObject* parent,
|
||||||
|
const QString& sourcePath,
|
||||||
|
const QString& targetPath,
|
||||||
|
const IPathMatcher::Ptr pathMatcher)
|
||||||
|
: Task(parent), m_sourcePath(sourcePath), m_targetPath(targetPath), m_pathMatcher(pathMatcher), m_copy(sourcePath, targetPath)
|
||||||
|
{
|
||||||
|
m_copy.matcher(m_pathMatcher.get()).whitelist(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::executeTask()
|
||||||
|
{
|
||||||
|
setStatus(tr("Scanning files..."));
|
||||||
|
|
||||||
|
// 1. Scan
|
||||||
|
// Check how many files we gotta copy
|
||||||
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
||||||
|
return m_copy(true); // dry run to collect amount of files
|
||||||
|
});
|
||||||
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
|
||||||
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::dryRunAborted);
|
||||||
|
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::dryRunFinished()
|
||||||
|
{
|
||||||
|
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::dryRunFinished);
|
||||||
|
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::dryRunAborted);
|
||||||
|
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
|
||||||
|
#else
|
||||||
|
if (!m_copyFuture.result()) {
|
||||||
|
#endif
|
||||||
|
emitFailed(tr("Failed to scan source path."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Copy
|
||||||
|
// Actually copy all files now.
|
||||||
|
m_toCopy = m_copy.totalCopied();
|
||||||
|
connect(&m_copy, &FS::copy::fileCopied, [&, this](const QString& relativeName) {
|
||||||
|
QString shortenedName = relativeName;
|
||||||
|
// shorten the filename to hopefully fit into one line
|
||||||
|
if (shortenedName.length() > 50)
|
||||||
|
shortenedName = relativeName.left(20) + "…" + relativeName.right(29);
|
||||||
|
setProgress(m_copy.totalCopied(), m_toCopy);
|
||||||
|
setStatus(tr("Copying %1…").arg(shortenedName));
|
||||||
|
});
|
||||||
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [&] {
|
||||||
|
return m_copy(false); // actually copy now
|
||||||
|
});
|
||||||
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
||||||
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::copyAborted);
|
||||||
|
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::dryRunAborted()
|
||||||
|
{
|
||||||
|
emitFailed(tr("Aborted"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::copyFinished()
|
||||||
|
{
|
||||||
|
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &DataMigrationTask::copyFinished);
|
||||||
|
disconnect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &DataMigrationTask::copyAborted);
|
||||||
|
|
||||||
|
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
|
||||||
|
if (!m_copyFuture.isValid() || !m_copyFuture.result()) {
|
||||||
|
#else
|
||||||
|
if (!m_copyFuture.result()) {
|
||||||
|
#endif
|
||||||
|
emitFailed(tr("Some paths could not be copied!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emitSucceeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataMigrationTask::copyAborted()
|
||||||
|
{
|
||||||
|
emitFailed(tr("Aborted"));
|
||||||
|
}
|
42
launcher/DataMigrationTask.h
Normal file
42
launcher/DataMigrationTask.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "pathmatcher/IPathMatcher.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QFutureWatcher>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Migrate existing data from other MMC-like launchers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DataMigrationTask : public Task {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DataMigrationTask(QObject* parent, const QString& sourcePath, const QString& targetPath, const IPathMatcher::Ptr pathmatcher);
|
||||||
|
~DataMigrationTask() override = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void executeTask() override;
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void dryRunFinished();
|
||||||
|
void dryRunAborted();
|
||||||
|
void copyFinished();
|
||||||
|
void copyAborted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString& m_sourcePath;
|
||||||
|
const QString& m_targetPath;
|
||||||
|
const IPathMatcher::Ptr m_pathMatcher;
|
||||||
|
|
||||||
|
FS::copy m_copy;
|
||||||
|
int m_toCopy = 0;
|
||||||
|
QFuture<bool> m_copyFuture;
|
||||||
|
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||||
|
};
|
@ -1,3 +1,37 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 dada513 <dada513@protonmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2022 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
#include "DesktopServices.h"
|
#include "DesktopServices.h"
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
@ -84,7 +118,14 @@ bool openDirectory(const QString &path, bool ensureExists)
|
|||||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
return QDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));
|
||||||
};
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
|
if(!isFlatpak())
|
||||||
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return f();
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
return f();
|
return f();
|
||||||
#endif
|
#endif
|
||||||
@ -98,7 +139,14 @@ bool openFile(const QString &path)
|
|||||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
return QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||||
};
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
|
if(!isFlatpak())
|
||||||
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return f();
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
return f();
|
return f();
|
||||||
#endif
|
#endif
|
||||||
@ -109,10 +157,17 @@ bool openFile(const QString &application, const QString &path, const QString &wo
|
|||||||
qDebug() << "Opening file" << path << "using" << application;
|
qDebug() << "Opening file" << path << "using" << application;
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||||
|
if(!isFlatpak())
|
||||||
|
{
|
||||||
return IndirectOpen([&]()
|
return IndirectOpen([&]()
|
||||||
{
|
{
|
||||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory);
|
return QProcess::startDetached(application, QStringList() << path, workingDirectory);
|
||||||
}, pid);
|
}, pid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
return QProcess::startDetached(application, QStringList() << path, workingDirectory, pid);
|
||||||
#endif
|
#endif
|
||||||
@ -122,11 +177,18 @@ bool run(const QString &application, const QStringList &args, const QString &wor
|
|||||||
{
|
{
|
||||||
qDebug() << "Running" << application << "with args" << args.join(' ');
|
qDebug() << "Running" << application << "with args" << args.join(' ');
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
|
if(!isFlatpak())
|
||||||
|
{
|
||||||
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
// FIXME: the pid here is fake. So if something depends on it, it will likely misbehave
|
||||||
return IndirectOpen([&]()
|
return IndirectOpen([&]()
|
||||||
{
|
{
|
||||||
return QProcess::startDetached(application, args, workingDirectory);
|
return QProcess::startDetached(application, args, workingDirectory);
|
||||||
}, pid);
|
}, pid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return QProcess::startDetached(application, args, workingDirectory, pid);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
return QProcess::startDetached(application, args, workingDirectory, pid);
|
return QProcess::startDetached(application, args, workingDirectory, pid);
|
||||||
#endif
|
#endif
|
||||||
@ -140,10 +202,26 @@ bool openUrl(const QUrl &url)
|
|||||||
return QDesktopServices::openUrl(url);
|
return QDesktopServices::openUrl(url);
|
||||||
};
|
};
|
||||||
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
|
||||||
|
if(!isFlatpak())
|
||||||
|
{
|
||||||
return IndirectOpen(f);
|
return IndirectOpen(f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return f();
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
return f();
|
return f();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isFlatpak()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
return QFile::exists("/.flatpak-info");
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,4 +33,6 @@ namespace DesktopServices
|
|||||||
* Open the URL, most likely in a browser. Maybe.
|
* Open the URL, most likely in a browser. Maybe.
|
||||||
*/
|
*/
|
||||||
bool openUrl(const QUrl &url);
|
bool openUrl(const QUrl &url);
|
||||||
|
|
||||||
|
bool isFlatpak();
|
||||||
}
|
}
|
||||||
|
47
launcher/FastFileIconProvider.cpp
Normal file
47
launcher/FastFileIconProvider.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FastFileIconProvider.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QStyle>
|
||||||
|
|
||||||
|
QIcon FastFileIconProvider::icon(const QFileInfo& info) const
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
|
||||||
|
bool link = info.isSymbolicLink() || info.isAlias() || info.isShortcut();
|
||||||
|
#else
|
||||||
|
// in versions prior to 6.4 we don't have access to isAlias
|
||||||
|
bool link = info.isSymLink();
|
||||||
|
#endif
|
||||||
|
QStyle::StandardPixmap icon;
|
||||||
|
|
||||||
|
if (info.isDir()) {
|
||||||
|
if (link)
|
||||||
|
icon = QStyle::SP_DirLinkIcon;
|
||||||
|
else
|
||||||
|
icon = QStyle::SP_DirIcon;
|
||||||
|
} else {
|
||||||
|
if (link)
|
||||||
|
icon = QStyle::SP_FileLinkIcon;
|
||||||
|
else
|
||||||
|
icon = QStyle::SP_FileIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QApplication::style()->standardIcon(icon);
|
||||||
|
}
|
26
launcher/FastFileIconProvider.h
Normal file
26
launcher/FastFileIconProvider.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFileIconProvider>
|
||||||
|
|
||||||
|
class FastFileIconProvider : public QFileIconProvider {
|
||||||
|
public:
|
||||||
|
QIcon icon(const QFileInfo& info) const override;
|
||||||
|
};
|
256
launcher/FileIgnoreProxy.cpp
Normal file
256
launcher/FileIgnoreProxy.cpp
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FileIgnoreProxy.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QFileSystemModel>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QStack>
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "SeparatorPrefixTree.h"
|
||||||
|
#include "StringUtils.h"
|
||||||
|
|
||||||
|
FileIgnoreProxy::FileIgnoreProxy(QString root, QObject* parent) : QSortFilterProxyModel(parent), root(root) {}
|
||||||
|
// NOTE: Sadly, we have to do sorting ourselves.
|
||||||
|
bool FileIgnoreProxy::lessThan(const QModelIndex& left, const QModelIndex& right) const
|
||||||
|
{
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
if (!fsm) {
|
||||||
|
return QSortFilterProxyModel::lessThan(left, right);
|
||||||
|
}
|
||||||
|
bool asc = sortOrder() == Qt::AscendingOrder ? true : false;
|
||||||
|
|
||||||
|
QFileInfo leftFileInfo = fsm->fileInfo(left);
|
||||||
|
QFileInfo rightFileInfo = fsm->fileInfo(right);
|
||||||
|
|
||||||
|
if (!leftFileInfo.isDir() && rightFileInfo.isDir()) {
|
||||||
|
return !asc;
|
||||||
|
}
|
||||||
|
if (leftFileInfo.isDir() && !rightFileInfo.isDir()) {
|
||||||
|
return asc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort and proxy model breaks the original model...
|
||||||
|
if (sortColumn() == 0) {
|
||||||
|
return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), Qt::CaseInsensitive) < 0;
|
||||||
|
}
|
||||||
|
if (sortColumn() == 1) {
|
||||||
|
auto leftSize = leftFileInfo.size();
|
||||||
|
auto rightSize = rightFileInfo.size();
|
||||||
|
if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir())) {
|
||||||
|
return StringUtils::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(), Qt::CaseInsensitive) < 0 ? asc : !asc;
|
||||||
|
}
|
||||||
|
return leftSize < rightSize;
|
||||||
|
}
|
||||||
|
return QSortFilterProxyModel::lessThan(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags FileIgnoreProxy::flags(const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return Qt::NoItemFlags;
|
||||||
|
|
||||||
|
auto sourceIndex = mapToSource(index);
|
||||||
|
Qt::ItemFlags flags = sourceIndex.flags();
|
||||||
|
if (index.column() == 0) {
|
||||||
|
flags |= Qt::ItemIsUserCheckable;
|
||||||
|
if (sourceIndex.model()->hasChildren(sourceIndex)) {
|
||||||
|
flags |= Qt::ItemIsAutoTristate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant FileIgnoreProxy::data(const QModelIndex& index, int role) const
|
||||||
|
{
|
||||||
|
QModelIndex sourceIndex = mapToSource(index);
|
||||||
|
|
||||||
|
if (index.column() == 0 && role == Qt::CheckStateRole) {
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||||
|
auto cover = blocked.cover(blockedPath);
|
||||||
|
if (!cover.isNull()) {
|
||||||
|
return QVariant(Qt::Unchecked);
|
||||||
|
} else if (blocked.exists(blockedPath)) {
|
||||||
|
return QVariant(Qt::PartiallyChecked);
|
||||||
|
} else {
|
||||||
|
return QVariant(Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceIndex.data(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||||
|
{
|
||||||
|
if (index.column() == 0 && role == Qt::CheckStateRole) {
|
||||||
|
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
|
||||||
|
return setFilterState(index, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex sourceIndex = mapToSource(index);
|
||||||
|
return QSortFilterProxyModel::sourceModel()->setData(sourceIndex, value, role);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileIgnoreProxy::relPath(const QString& path) const
|
||||||
|
{
|
||||||
|
return QDir(root).relativeFilePath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::setFilterState(QModelIndex index, Qt::CheckState state)
|
||||||
|
{
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
|
||||||
|
if (!fsm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex sourceIndex = mapToSource(index);
|
||||||
|
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||||
|
bool changed = false;
|
||||||
|
if (state == Qt::Unchecked) {
|
||||||
|
// blocking a path
|
||||||
|
auto& node = blocked.insert(blockedPath);
|
||||||
|
// get rid of all blocked nodes below
|
||||||
|
node.clear();
|
||||||
|
changed = true;
|
||||||
|
} else if (state == Qt::Checked || state == Qt::PartiallyChecked) {
|
||||||
|
if (!blocked.remove(blockedPath)) {
|
||||||
|
auto cover = blocked.cover(blockedPath);
|
||||||
|
qDebug() << "Blocked by cover" << cover;
|
||||||
|
// uncover
|
||||||
|
blocked.remove(cover);
|
||||||
|
// block all contents, except for any cover
|
||||||
|
QModelIndex rootIndex = fsm->index(FS::PathCombine(root, cover));
|
||||||
|
QModelIndex doing = rootIndex;
|
||||||
|
int row = 0;
|
||||||
|
QStack<QModelIndex> todo;
|
||||||
|
while (1) {
|
||||||
|
auto node = fsm->index(row, 0, doing);
|
||||||
|
if (!node.isValid()) {
|
||||||
|
if (!todo.size()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
doing = todo.pop();
|
||||||
|
row = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto relpath = relPath(fsm->filePath(node));
|
||||||
|
if (blockedPath.startsWith(relpath)) // cover found?
|
||||||
|
{
|
||||||
|
// continue processing cover later
|
||||||
|
todo.push(node);
|
||||||
|
} else {
|
||||||
|
// or just block this one.
|
||||||
|
blocked.insert(relpath);
|
||||||
|
}
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
// update the thing
|
||||||
|
emit dataChanged(index, index, { Qt::CheckStateRole });
|
||||||
|
// update everything above index
|
||||||
|
QModelIndex up = index.parent();
|
||||||
|
while (1) {
|
||||||
|
if (!up.isValid())
|
||||||
|
break;
|
||||||
|
emit dataChanged(up, up, { Qt::CheckStateRole });
|
||||||
|
up = up.parent();
|
||||||
|
}
|
||||||
|
// and everything below the index
|
||||||
|
QModelIndex doing = index;
|
||||||
|
int row = 0;
|
||||||
|
QStack<QModelIndex> todo;
|
||||||
|
while (1) {
|
||||||
|
auto node = this->index(row, 0, doing);
|
||||||
|
if (!node.isValid()) {
|
||||||
|
if (!todo.size()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
doing = todo.pop();
|
||||||
|
row = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit dataChanged(node, node, { Qt::CheckStateRole });
|
||||||
|
todo.push(node);
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
// siblings and unrelated nodes are ignored
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::shouldExpand(QModelIndex index)
|
||||||
|
{
|
||||||
|
QModelIndex sourceIndex = mapToSource(index);
|
||||||
|
QFileSystemModel* fsm = qobject_cast<QFileSystemModel*>(sourceModel());
|
||||||
|
if (!fsm) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||||
|
auto found = blocked.find(blockedPath);
|
||||||
|
if (found) {
|
||||||
|
return !found->leaf();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileIgnoreProxy::setBlockedPaths(QStringList paths)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
blocked.clear();
|
||||||
|
blocked.insert(paths);
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileIgnoreProxy::filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(source_parent)
|
||||||
|
|
||||||
|
// adjust the columns you want to filter out here
|
||||||
|
// return false for those that will be hidden
|
||||||
|
if (source_column == 2 || source_column == 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
72
launcher/FileIgnoreProxy.h
Normal file
72
launcher/FileIgnoreProxy.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include "SeparatorPrefixTree.h"
|
||||||
|
|
||||||
|
class FileIgnoreProxy : public QSortFilterProxyModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileIgnoreProxy(QString root, QObject* parent);
|
||||||
|
// NOTE: Sadly, we have to do sorting ourselves.
|
||||||
|
bool lessThan(const QModelIndex& left, const QModelIndex& right) const;
|
||||||
|
|
||||||
|
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||||
|
|
||||||
|
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
|
||||||
|
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
|
||||||
|
|
||||||
|
QString relPath(const QString& path) const;
|
||||||
|
|
||||||
|
bool setFilterState(QModelIndex index, Qt::CheckState state);
|
||||||
|
|
||||||
|
bool shouldExpand(QModelIndex index);
|
||||||
|
|
||||||
|
void setBlockedPaths(QStringList paths);
|
||||||
|
|
||||||
|
inline const SeparatorPrefixTree<'/'>& blockedPaths() const { return blocked; }
|
||||||
|
inline SeparatorPrefixTree<'/'>& blockedPaths() { return blocked; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsColumn(int source_column, const QModelIndex& source_parent) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString root;
|
||||||
|
SeparatorPrefixTree<'/'> blocked;
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
@ -1,18 +1,56 @@
|
|||||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (C) 2022 TheKodeToad <TheKodeToad@proton.me>
|
||||||
|
* Copyright (C) 2022 Rachel Powers <508861+Ryex@users.noreply.github.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Exception.h"
|
#include "Exception.h"
|
||||||
#include "pathmatcher/IPathMatcher.h"
|
#include "pathmatcher/IPathMatcher.h"
|
||||||
|
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFlags>
|
#include <QFlags>
|
||||||
|
#include <QLocalServer>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
namespace FS
|
namespace FS {
|
||||||
{
|
|
||||||
|
|
||||||
class FileSystemException : public ::Exception
|
class FileSystemException : public ::Exception {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
FileSystemException(const QString& message) : Exception(message) {}
|
FileSystemException(const QString& message) : Exception(message) {}
|
||||||
};
|
};
|
||||||
@ -44,49 +82,214 @@ bool ensureFilePathExists(QString filenamepath);
|
|||||||
*/
|
*/
|
||||||
bool ensureFolderPathExists(QString filenamepath);
|
bool ensureFolderPathExists(QString filenamepath);
|
||||||
|
|
||||||
class copy
|
/**
|
||||||
{
|
* @brief Copies a directory and it's contents from src to dest
|
||||||
|
*/
|
||||||
|
class copy : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
copy(const QString & src, const QString & dst)
|
copy(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
||||||
{
|
{
|
||||||
m_src = src;
|
m_src.setPath(src);
|
||||||
m_dst = dst;
|
m_dst.setPath(dst);
|
||||||
}
|
}
|
||||||
copy& followSymlinks(const bool follow)
|
copy& followSymlinks(const bool follow)
|
||||||
{
|
{
|
||||||
m_followSymlinks = follow;
|
m_followSymlinks = follow;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
copy & blacklist(const IPathMatcher * filter)
|
copy& matcher(const IPathMatcher* filter)
|
||||||
{
|
{
|
||||||
m_blacklist = filter;
|
m_matcher = filter;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
bool operator()()
|
copy& whitelist(bool whitelist)
|
||||||
{
|
{
|
||||||
return operator()(QString());
|
m_whitelist = whitelist;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
|
int totalCopied() { return m_copied; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fileCopied(const QString& relativeName);
|
||||||
|
// TODO: maybe add a "shouldCopy" signal in the future?
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool operator()(const QString &offset);
|
bool operator()(const QString& offset, bool dryRun = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_followSymlinks = true;
|
bool m_followSymlinks = true;
|
||||||
const IPathMatcher * m_blacklist = nullptr;
|
const IPathMatcher* m_matcher = nullptr;
|
||||||
|
bool m_whitelist = false;
|
||||||
QDir m_src;
|
QDir m_src;
|
||||||
QDir m_dst;
|
QDir m_dst;
|
||||||
|
int m_copied;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LinkPair {
|
||||||
|
QString src;
|
||||||
|
QString dst;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LinkResult {
|
||||||
|
QString src;
|
||||||
|
QString dst;
|
||||||
|
QString err_msg;
|
||||||
|
int err_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExternalLinkFileProcess : public QThread {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ExternalLinkFileProcess(QString server, bool useHardLinks, QObject* parent = nullptr)
|
||||||
|
: QThread(parent), m_useHardLinks(useHardLinks), m_server(server)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
runLinkFile();
|
||||||
|
emit processExited();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void processExited();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void runLinkFile();
|
||||||
|
|
||||||
|
bool m_useHardLinks = false;
|
||||||
|
|
||||||
|
QString m_server;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief links (a file / a directory and it's contents) from src to dest
|
||||||
|
*/
|
||||||
|
class create_link : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
create_link(const QList<LinkPair> path_pairs, QObject* parent = nullptr) : QObject(parent) { m_path_pairs.append(path_pairs); }
|
||||||
|
create_link(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
||||||
|
{
|
||||||
|
LinkPair pair = { src, dst };
|
||||||
|
m_path_pairs.append(pair);
|
||||||
|
}
|
||||||
|
create_link& useHardLinks(const bool useHard)
|
||||||
|
{
|
||||||
|
m_useHardLinks = useHard;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& matcher(const IPathMatcher* filter)
|
||||||
|
{
|
||||||
|
m_matcher = filter;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& whitelist(bool whitelist)
|
||||||
|
{
|
||||||
|
m_whitelist = whitelist;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& linkRecursively(bool recursive)
|
||||||
|
{
|
||||||
|
m_recursive = recursive;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& setMaxDepth(int depth)
|
||||||
|
{
|
||||||
|
m_max_depth = depth;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
create_link& debug(bool d)
|
||||||
|
{
|
||||||
|
m_debug = d;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code getOSError() { return m_os_err; }
|
||||||
|
|
||||||
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
|
int totalLinked() { return m_linked; }
|
||||||
|
|
||||||
|
void runPrivileged() { runPrivileged(QString()); }
|
||||||
|
void runPrivileged(const QString& offset);
|
||||||
|
|
||||||
|
QList<LinkResult> getResults() { return m_path_results; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fileLinked(const QString& srcName, const QString& dstName);
|
||||||
|
void linkFailed(const QString& srcName, const QString& dstName, const QString& err_msg, int err_value);
|
||||||
|
void finished();
|
||||||
|
void finishedPrivileged(bool gotResults);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool operator()(const QString& offset, bool dryRun = false);
|
||||||
|
void make_link_list(const QString& offset);
|
||||||
|
bool make_links();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_useHardLinks = false;
|
||||||
|
const IPathMatcher* m_matcher = nullptr;
|
||||||
|
bool m_whitelist = false;
|
||||||
|
bool m_recursive = true;
|
||||||
|
|
||||||
|
/// @brief >= -1 = infinite, 0 = link files at src/* to dest/*, 1 = link files at src/*/* to dest/*/*, etc.
|
||||||
|
int m_max_depth = -1;
|
||||||
|
|
||||||
|
QList<LinkPair> m_path_pairs;
|
||||||
|
QList<LinkResult> m_path_results;
|
||||||
|
QList<LinkPair> m_links_to_make;
|
||||||
|
|
||||||
|
int m_linked;
|
||||||
|
bool m_debug = false;
|
||||||
|
std::error_code m_os_err;
|
||||||
|
|
||||||
|
QLocalServer m_linkServer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief moves a file by renaming it
|
||||||
|
* @param source source file path
|
||||||
|
* @param dest destination filepath
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool move(const QString& source, const QString& dest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a folder recursively
|
* Delete a folder recursively
|
||||||
*/
|
*/
|
||||||
bool deletePath(QString path);
|
bool deletePath(QString path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trash a folder / file
|
||||||
|
*/
|
||||||
|
bool trash(QString path, QString* pathInTrash = nullptr);
|
||||||
|
|
||||||
QString PathCombine(const QString& path1, const QString& path2);
|
QString PathCombine(const QString& path1, const QString& path2);
|
||||||
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
|
QString PathCombine(const QString& path1, const QString& path2, const QString& path3);
|
||||||
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4);
|
QString PathCombine(const QString& path1, const QString& path2, const QString& path3, const QString& path4);
|
||||||
|
|
||||||
QString AbsolutePath(QString path);
|
QString AbsolutePath(const QString& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief depth of path. "foo.txt" -> 0 , "bar/foo.txt" -> 1, /baz/bar/foo.txt -> 2, etc.
|
||||||
|
*
|
||||||
|
* @param path path to measure
|
||||||
|
* @return int number of components before base path
|
||||||
|
*/
|
||||||
|
int pathDepth(const QString& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief cut off segments of path until it is a max of length depth
|
||||||
|
*
|
||||||
|
* @param path path to truncate
|
||||||
|
* @param depth max depth of new path
|
||||||
|
* @return QString truncated path
|
||||||
|
*/
|
||||||
|
QString pathTruncate(const QString& path, int depth);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve an executable
|
* Resolve an executable
|
||||||
@ -120,8 +323,202 @@ bool checkProblemticPathJava(QDir folder);
|
|||||||
// Get the Directory representing the User's Desktop
|
// Get the Directory representing the User's Desktop
|
||||||
QString getDesktopDir();
|
QString getDesktopDir();
|
||||||
|
|
||||||
// Create a shortcut at *location*, pointing to *dest* called with the arguments *args*
|
// Overrides one folder with the contents of another, preserving items exclusive to the first folder
|
||||||
// call it *name* and assign it the icon *icon*
|
// Equivalent to doing QDir::rename, but allowing for overrides
|
||||||
// return true if operation succeeded
|
bool overrideFolder(QString overwritten_path, QString override_path);
|
||||||
bool createShortCut(QString location, QString dest, QStringList args, QString name, QString iconLocation);
|
|
||||||
|
/**
|
||||||
|
* Creates a shortcut to the specified target file at the specified destination path.
|
||||||
|
*/
|
||||||
|
bool createShortcut(QString destination, QString target, QStringList args, QString name, QString icon);
|
||||||
|
|
||||||
|
enum class FilesystemType {
|
||||||
|
FAT,
|
||||||
|
NTFS,
|
||||||
|
REFS,
|
||||||
|
EXT,
|
||||||
|
EXT_2_OLD,
|
||||||
|
EXT_2_3_4,
|
||||||
|
XFS,
|
||||||
|
BTRFS,
|
||||||
|
NFS,
|
||||||
|
ZFS,
|
||||||
|
APFS,
|
||||||
|
HFS,
|
||||||
|
HFSPLUS,
|
||||||
|
HFSX,
|
||||||
|
FUSEBLK,
|
||||||
|
F2FS,
|
||||||
|
UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ordered Mapping of enum types to reported filesystem names
|
||||||
|
* this mapping is non exsaustive, it just attempts to capture the filesystems which could be reasonalbly be in use .
|
||||||
|
* all string values are in uppercase, use `QString.toUpper()` or equivalent during lookup.
|
||||||
|
*
|
||||||
|
* QMap is ordered
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static const QMap<FilesystemType, QStringList> s_filesystem_type_names = {
|
||||||
|
{FilesystemType::FAT, { "FAT" }},
|
||||||
|
{FilesystemType::NTFS, { "NTFS" }},
|
||||||
|
{FilesystemType::REFS, { "REFS" }},
|
||||||
|
{FilesystemType::EXT_2_OLD, { "EXT_2_OLD", "EXT2_OLD" }},
|
||||||
|
{FilesystemType::EXT_2_3_4, { "EXT2/3/4", "EXT_2_3_4", "EXT2", "EXT3", "EXT4" }},
|
||||||
|
{FilesystemType::EXT, { "EXT" }},
|
||||||
|
{FilesystemType::XFS, { "XFS" }},
|
||||||
|
{FilesystemType::BTRFS, { "BTRFS" }},
|
||||||
|
{FilesystemType::NFS, { "NFS" }},
|
||||||
|
{FilesystemType::ZFS, { "ZFS" }},
|
||||||
|
{FilesystemType::APFS, { "APFS" }},
|
||||||
|
{FilesystemType::HFS, { "HFS" }},
|
||||||
|
{FilesystemType::HFSPLUS, { "HFSPLUS" }},
|
||||||
|
{FilesystemType::HFSX, { "HFSX" }},
|
||||||
|
{FilesystemType::FUSEBLK, { "FUSEBLK" }},
|
||||||
|
{FilesystemType::F2FS, { "F2FS" }},
|
||||||
|
{FilesystemType::UNKNOWN, { "UNKNOWN" }}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the string name of Filesystem enum object
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @return QString
|
||||||
|
*/
|
||||||
|
QString getFilesystemTypeName(FilesystemType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the Filesystem enum object from a name
|
||||||
|
* Does a lookup of the type name and returns an exact match
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @return FilesystemType
|
||||||
|
*/
|
||||||
|
FilesystemType getFilesystemType(const QString& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the Filesystem enum object from a name
|
||||||
|
* Does a fuzzy lookup of the type name and returns an apropreate match
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* @return FilesystemType
|
||||||
|
*/
|
||||||
|
FilesystemType getFilesystemTypeFuzzy(const QString& name);
|
||||||
|
|
||||||
|
struct FilesystemInfo {
|
||||||
|
FilesystemType fsType = FilesystemType::UNKNOWN;
|
||||||
|
QString fsTypeName;
|
||||||
|
int blockSize;
|
||||||
|
qint64 bytesAvailable;
|
||||||
|
qint64 bytesFree;
|
||||||
|
qint64 bytesTotal;
|
||||||
|
QString name;
|
||||||
|
QString rootPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief path to the near ancestor that exists
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
QString nearestExistentAncestor(const QString& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief colect information about the filesystem under a file
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
FilesystemInfo statFS(const QString& path);
|
||||||
|
|
||||||
|
static const QList<FilesystemType> s_clone_filesystems = { FilesystemType::BTRFS, FilesystemType::APFS, FilesystemType::ZFS,
|
||||||
|
FilesystemType::XFS, FilesystemType::REFS };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if the Filesystem is reflink/clone capable
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool canCloneOnFS(const QString& path);
|
||||||
|
bool canCloneOnFS(const FilesystemInfo& info);
|
||||||
|
bool canCloneOnFS(FilesystemType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if the Filesystems are reflink/clone capable and both are on the same device
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool canClone(const QString& src, const QString& dst);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copies a directory and it's contents from src to dest
|
||||||
|
*/
|
||||||
|
class clone : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
clone(const QString& src, const QString& dst, QObject* parent = nullptr) : QObject(parent)
|
||||||
|
{
|
||||||
|
m_src.setPath(src);
|
||||||
|
m_dst.setPath(dst);
|
||||||
}
|
}
|
||||||
|
clone& matcher(const IPathMatcher* filter)
|
||||||
|
{
|
||||||
|
m_matcher = filter;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
clone& whitelist(bool whitelist)
|
||||||
|
{
|
||||||
|
m_whitelist = whitelist;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(bool dryRun = false) { return operator()(QString(), dryRun); }
|
||||||
|
|
||||||
|
int totalCloned() { return m_cloned; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void fileCloned(const QString& src, const QString& dst);
|
||||||
|
void cloneFailed(const QString& src, const QString& dst);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool operator()(const QString& offset, bool dryRun = false);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const IPathMatcher* m_matcher = nullptr;
|
||||||
|
bool m_whitelist = false;
|
||||||
|
QDir m_src;
|
||||||
|
QDir m_dst;
|
||||||
|
int m_cloned;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief clone/reflink file from src to dst
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool clone_file(const QString& src, const QString& dst, std::error_code& ec);
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
bool win_ioctl_clone(const std::wstring& src_path, const std::wstring& dst_path, std::error_code& ec);
|
||||||
|
#elif defined(Q_OS_LINUX)
|
||||||
|
bool linux_ficlone(const std::string& src_path, const std::string& dst_path, std::error_code& ec);
|
||||||
|
#elif defined(Q_OS_MACOS) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD)
|
||||||
|
bool macos_bsd_clonefile(const std::string& src_path, const std::string& dst_path, std::error_code& ec);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const QList<FilesystemType> s_non_link_filesystems = {
|
||||||
|
FilesystemType::FAT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if the Filesystem is symlink capable
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool canLinkOnFS(const QString& path);
|
||||||
|
bool canLinkOnFS(const FilesystemInfo& info);
|
||||||
|
bool canLinkOnFS(FilesystemType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief if the Filesystem is symlink capable on both ends
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool canLink(const QString& src, const QString& dst);
|
||||||
|
|
||||||
|
uintmax_t hardLinkCount(const QString& path);
|
||||||
|
|
||||||
|
} // namespace FS
|
||||||
|
@ -1,164 +0,0 @@
|
|||||||
#include <QTest>
|
|
||||||
#include <QTemporaryDir>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include "TestUtil.h"
|
|
||||||
|
|
||||||
#include "FileSystem.h"
|
|
||||||
|
|
||||||
class FileSystemTest : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
const QString bothSlash = "/foo/";
|
|
||||||
const QString trailingSlash = "foo/";
|
|
||||||
const QString leadingSlash = "/foo";
|
|
||||||
|
|
||||||
private
|
|
||||||
slots:
|
|
||||||
void test_pathCombine()
|
|
||||||
{
|
|
||||||
QCOMPARE(QString("/foo/foo"), FS::PathCombine(bothSlash, bothSlash));
|
|
||||||
QCOMPARE(QString("foo/foo"), FS::PathCombine(trailingSlash, trailingSlash));
|
|
||||||
QCOMPARE(QString("/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash));
|
|
||||||
|
|
||||||
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(bothSlash, bothSlash, bothSlash));
|
|
||||||
QCOMPARE(QString("foo/foo/foo"), FS::PathCombine(trailingSlash, trailingSlash, trailingSlash));
|
|
||||||
QCOMPARE(QString("/foo/foo/foo"), FS::PathCombine(leadingSlash, leadingSlash, leadingSlash));
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_PathCombine1_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<QString>("result");
|
|
||||||
QTest::addColumn<QString>("path1");
|
|
||||||
QTest::addColumn<QString>("path2");
|
|
||||||
|
|
||||||
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc/def" << "ghi/jkl";
|
|
||||||
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/def/" << "ghi/jkl";
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
QTest::newRow("win native, from C:") << "C:/abc" << "C:" << "abc";
|
|
||||||
QTest::newRow("win native 1") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def" << "ghi\\jkl";
|
|
||||||
QTest::newRow("win native 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\def\\" << "ghi\\jkl";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_PathCombine1()
|
|
||||||
{
|
|
||||||
QFETCH(QString, result);
|
|
||||||
QFETCH(QString, path1);
|
|
||||||
QFETCH(QString, path2);
|
|
||||||
|
|
||||||
QCOMPARE(FS::PathCombine(path1, path2), result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_PathCombine2_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<QString>("result");
|
|
||||||
QTest::addColumn<QString>("path1");
|
|
||||||
QTest::addColumn<QString>("path2");
|
|
||||||
QTest::addColumn<QString>("path3");
|
|
||||||
|
|
||||||
QTest::newRow("qt 1") << "/abc/def/ghi/jkl" << "/abc" << "def" << "ghi/jkl";
|
|
||||||
QTest::newRow("qt 2") << "/abc/def/ghi/jkl" << "/abc/" << "def" << "ghi/jkl";
|
|
||||||
QTest::newRow("qt 3") << "/abc/def/ghi/jkl" << "/abc" << "def/" << "ghi/jkl";
|
|
||||||
QTest::newRow("qt 4") << "/abc/def/ghi/jkl" << "/abc/" << "def/" << "ghi/jkl";
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
QTest::newRow("win 1") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def" << "ghi\\jkl";
|
|
||||||
QTest::newRow("win 2") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
|
|
||||||
QTest::newRow("win 3") << "C:/abc/def/ghi/jkl" << "C:\\abc" << "def\\" << "ghi\\jkl";
|
|
||||||
QTest::newRow("win 4") << "C:/abc/def/ghi/jkl" << "C:\\abc\\" << "def" << "ghi\\jkl";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_PathCombine2()
|
|
||||||
{
|
|
||||||
QFETCH(QString, result);
|
|
||||||
QFETCH(QString, path1);
|
|
||||||
QFETCH(QString, path2);
|
|
||||||
QFETCH(QString, path3);
|
|
||||||
|
|
||||||
QCOMPARE(FS::PathCombine(path1, path2, path3), result);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_copy()
|
|
||||||
{
|
|
||||||
QString folder = QFINDTESTDATA("data/test_folder");
|
|
||||||
auto f = [&folder]()
|
|
||||||
{
|
|
||||||
QTemporaryDir tempDir;
|
|
||||||
tempDir.setAutoRemove(true);
|
|
||||||
qDebug() << "From:" << folder << "To:" << tempDir.path();
|
|
||||||
|
|
||||||
QDir target_dir(FS::PathCombine(tempDir.path(), "test_folder"));
|
|
||||||
qDebug() << tempDir.path();
|
|
||||||
qDebug() << target_dir.path();
|
|
||||||
FS::copy c(folder, target_dir.path());
|
|
||||||
c();
|
|
||||||
|
|
||||||
for(auto entry: target_dir.entryList())
|
|
||||||
{
|
|
||||||
qDebug() << entry;
|
|
||||||
}
|
|
||||||
QVERIFY(target_dir.entryList().contains("pack.mcmeta"));
|
|
||||||
QVERIFY(target_dir.entryList().contains("assets"));
|
|
||||||
};
|
|
||||||
|
|
||||||
// first try variant without trailing /
|
|
||||||
QVERIFY(!folder.endsWith('/'));
|
|
||||||
f();
|
|
||||||
|
|
||||||
// then variant with trailing /
|
|
||||||
folder.append('/');
|
|
||||||
QVERIFY(folder.endsWith('/'));
|
|
||||||
f();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_getDesktop()
|
|
||||||
{
|
|
||||||
QCOMPARE(FS::getDesktopDir(), QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is only valid on linux
|
|
||||||
// FIXME: implement on windows, OSX, then test.
|
|
||||||
#if defined(Q_OS_LINUX)
|
|
||||||
void test_createShortcut_data()
|
|
||||||
{
|
|
||||||
QTest::addColumn<QString>("location");
|
|
||||||
QTest::addColumn<QString>("dest");
|
|
||||||
QTest::addColumn<QStringList>("args");
|
|
||||||
QTest::addColumn<QString>("name");
|
|
||||||
QTest::addColumn<QString>("iconLocation");
|
|
||||||
QTest::addColumn<QByteArray>("result");
|
|
||||||
|
|
||||||
QTest::newRow("unix") << QDir::currentPath()
|
|
||||||
<< "asdfDest"
|
|
||||||
<< (QStringList() << "arg1" << "arg2")
|
|
||||||
<< "asdf"
|
|
||||||
<< QString()
|
|
||||||
#if defined(Q_OS_LINUX)
|
|
||||||
<< GET_TEST_FILE("data/FileSystem-test_createShortcut-unix")
|
|
||||||
#elif defined(Q_OS_WIN)
|
|
||||||
<< QByteArray()
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_createShortcut()
|
|
||||||
{
|
|
||||||
QFETCH(QString, location);
|
|
||||||
QFETCH(QString, dest);
|
|
||||||
QFETCH(QStringList, args);
|
|
||||||
QFETCH(QString, name);
|
|
||||||
QFETCH(QString, iconLocation);
|
|
||||||
QFETCH(QByteArray, result);
|
|
||||||
|
|
||||||
QVERIFY(FS::createShortCut(location, dest, args, name, iconLocation));
|
|
||||||
QCOMPARE(QString::fromLocal8Bit(TestsInternal::readFile(location + QDir::separator() + name + ".desktop")), QString::fromLocal8Bit(result));
|
|
||||||
|
|
||||||
//QDir().remove(location);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(FileSystemTest)
|
|
||||||
|
|
||||||
#include "FileSystem_test.moc"
|
|
@ -1,3 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "GZip.h"
|
#include "GZip.h"
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
@ -37,7 +72,7 @@ bool GZip::unzip(const QByteArray &compressedBytes, QByteArray &uncompressedByte
|
|||||||
uncompLength *= 2;
|
uncompLength *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
strm.next_out = (Bytef *)(uncompressedBytes.data() + strm.total_out);
|
strm.next_out = reinterpret_cast<Bytef *>((uncompressedBytes.data() + strm.total_out));
|
||||||
strm.avail_out = uncompLength - strm.total_out;
|
strm.avail_out = uncompLength - strm.total_out;
|
||||||
|
|
||||||
// Inflate another chunk.
|
// Inflate another chunk.
|
||||||
@ -67,7 +102,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned compLength = std::min(uncompressedBytes.size(), 16);
|
unsigned compLength = qMin(uncompressedBytes.size(), 16);
|
||||||
compressedBytes.clear();
|
compressedBytes.clear();
|
||||||
compressedBytes.resize(compLength);
|
compressedBytes.resize(compLength);
|
||||||
|
|
||||||
@ -94,7 +129,7 @@ bool GZip::zip(const QByteArray &uncompressedBytes, QByteArray &compressedBytes)
|
|||||||
{
|
{
|
||||||
compressedBytes.resize(compressedBytes.size() * 2);
|
compressedBytes.resize(compressedBytes.size() * 2);
|
||||||
}
|
}
|
||||||
zs.next_out = (Bytef *) (compressedBytes.data() + offset);
|
zs.next_out = reinterpret_cast<Bytef*>((compressedBytes.data() + offset));
|
||||||
temp = zs.avail_out = compressedBytes.size() - offset;
|
temp = zs.avail_out = compressedBytes.size() - offset;
|
||||||
ret = deflate(&zs, Z_FINISH);
|
ret = deflate(&zs, Z_FINISH);
|
||||||
offset += temp - zs.avail_out;
|
offset += temp - zs.avail_out;
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <hoedown/html.h>
|
|
||||||
#include <hoedown/document.h>
|
|
||||||
#include <QString>
|
|
||||||
#include <QByteArray>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* hoedown wrapper, because dealing with resource lifetime in C is stupid
|
|
||||||
*/
|
|
||||||
class HoeDown
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
class buffer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
buffer(size_t unit = 4096)
|
|
||||||
{
|
|
||||||
buf = hoedown_buffer_new(unit);
|
|
||||||
}
|
|
||||||
~buffer()
|
|
||||||
{
|
|
||||||
hoedown_buffer_free(buf);
|
|
||||||
}
|
|
||||||
const char * cstr()
|
|
||||||
{
|
|
||||||
return hoedown_buffer_cstr(buf);
|
|
||||||
}
|
|
||||||
void put(QByteArray input)
|
|
||||||
{
|
|
||||||
hoedown_buffer_put(buf, (uint8_t *) input.data(), input.size());
|
|
||||||
}
|
|
||||||
const uint8_t * data() const
|
|
||||||
{
|
|
||||||
return buf->data;
|
|
||||||
}
|
|
||||||
size_t size() const
|
|
||||||
{
|
|
||||||
return buf->size;
|
|
||||||
}
|
|
||||||
hoedown_buffer * buf;
|
|
||||||
} ib, ob;
|
|
||||||
HoeDown()
|
|
||||||
{
|
|
||||||
renderer = hoedown_html_renderer_new((hoedown_html_flags) 0,0);
|
|
||||||
document = hoedown_document_new(renderer, (hoedown_extensions) 0, 8);
|
|
||||||
}
|
|
||||||
~HoeDown()
|
|
||||||
{
|
|
||||||
hoedown_document_free(document);
|
|
||||||
hoedown_html_renderer_free(renderer);
|
|
||||||
}
|
|
||||||
QString process(QByteArray input)
|
|
||||||
{
|
|
||||||
ib.put(input);
|
|
||||||
hoedown_document_render(document, ob.buf, ib.data(), ib.size());
|
|
||||||
return ob.cstr();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
hoedown_document * document;
|
|
||||||
hoedown_renderer * renderer;
|
|
||||||
};
|
|
194
launcher/InstanceCopyPrefs.cpp
Normal file
194
launcher/InstanceCopyPrefs.cpp
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
//
|
||||||
|
// Created by marcelohdez on 10/22/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "InstanceCopyPrefs.h"
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::allTrue() const
|
||||||
|
{
|
||||||
|
return copySaves &&
|
||||||
|
keepPlaytime &&
|
||||||
|
copyGameOptions &&
|
||||||
|
copyResourcePacks &&
|
||||||
|
copyShaderPacks &&
|
||||||
|
copyServers &&
|
||||||
|
copyMods &&
|
||||||
|
copyScreenshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns a single RegEx string of the selected folders/files to filter out (ex: ".minecraft/saves|.minecraft/server.dat")
|
||||||
|
QString InstanceCopyPrefs::getSelectedFiltersAsRegex() const
|
||||||
|
{
|
||||||
|
return getSelectedFiltersAsRegex({});
|
||||||
|
}
|
||||||
|
QString InstanceCopyPrefs::getSelectedFiltersAsRegex(const QStringList& additionalFilters) const
|
||||||
|
{
|
||||||
|
QStringList filters;
|
||||||
|
|
||||||
|
if(!copySaves)
|
||||||
|
filters << "saves";
|
||||||
|
|
||||||
|
if(!copyGameOptions)
|
||||||
|
filters << "options.txt";
|
||||||
|
|
||||||
|
if(!copyResourcePacks)
|
||||||
|
filters << "resourcepacks" << "texturepacks";
|
||||||
|
|
||||||
|
if(!copyShaderPacks)
|
||||||
|
filters << "shaderpacks";
|
||||||
|
|
||||||
|
if(!copyServers)
|
||||||
|
filters << "servers.dat" << "servers.dat_old" << "server-resource-packs";
|
||||||
|
|
||||||
|
if(!copyMods)
|
||||||
|
filters << "coremods" << "mods" << "config";
|
||||||
|
|
||||||
|
if(!copyScreenshots)
|
||||||
|
filters << "screenshots";
|
||||||
|
|
||||||
|
for (auto filter : additionalFilters) {
|
||||||
|
filters << filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have any filters to add, join them as a single regex string to return:
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
const QString MC_ROOT = "[.]?minecraft/";
|
||||||
|
// Ensure first filter starts with root, then join other filters with OR regex before root (ex: ".minecraft/saves|.minecraft/mods"):
|
||||||
|
return MC_ROOT + filters.join("|" + MC_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= Getters =======
|
||||||
|
bool InstanceCopyPrefs::isCopySavesEnabled() const
|
||||||
|
{
|
||||||
|
return copySaves;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isKeepPlaytimeEnabled() const
|
||||||
|
{
|
||||||
|
return keepPlaytime;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyGameOptionsEnabled() const
|
||||||
|
{
|
||||||
|
return copyGameOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyResourcePacksEnabled() const
|
||||||
|
{
|
||||||
|
return copyResourcePacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyShaderPacksEnabled() const
|
||||||
|
{
|
||||||
|
return copyShaderPacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyServersEnabled() const
|
||||||
|
{
|
||||||
|
return copyServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyModsEnabled() const
|
||||||
|
{
|
||||||
|
return copyMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isCopyScreenshotsEnabled() const
|
||||||
|
{
|
||||||
|
return copyScreenshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isUseSymLinksEnabled() const
|
||||||
|
{
|
||||||
|
return useSymLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isUseHardLinksEnabled() const
|
||||||
|
{
|
||||||
|
return useHardLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isLinkRecursivelyEnabled() const
|
||||||
|
{
|
||||||
|
return linkRecursively;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isDontLinkSavesEnabled() const
|
||||||
|
{
|
||||||
|
return dontLinkSaves;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceCopyPrefs::isUseCloneEnabled() const
|
||||||
|
{
|
||||||
|
return useClone;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======= Setters =======
|
||||||
|
void InstanceCopyPrefs::enableCopySaves(bool b)
|
||||||
|
{
|
||||||
|
copySaves = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableKeepPlaytime(bool b)
|
||||||
|
{
|
||||||
|
keepPlaytime = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyGameOptions(bool b)
|
||||||
|
{
|
||||||
|
copyGameOptions = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyResourcePacks(bool b)
|
||||||
|
{
|
||||||
|
copyResourcePacks = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyShaderPacks(bool b)
|
||||||
|
{
|
||||||
|
copyShaderPacks = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyServers(bool b)
|
||||||
|
{
|
||||||
|
copyServers = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyMods(bool b)
|
||||||
|
{
|
||||||
|
copyMods = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableCopyScreenshots(bool b)
|
||||||
|
{
|
||||||
|
copyScreenshots = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableUseSymLinks(bool b)
|
||||||
|
{
|
||||||
|
useSymLinks = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableLinkRecursively(bool b)
|
||||||
|
{
|
||||||
|
linkRecursively = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableUseHardLinks(bool b)
|
||||||
|
{
|
||||||
|
useHardLinks = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableDontLinkSaves(bool b)
|
||||||
|
{
|
||||||
|
dontLinkSaves = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceCopyPrefs::enableUseClone(bool b)
|
||||||
|
{
|
||||||
|
useClone = b;
|
||||||
|
}
|
57
launcher/InstanceCopyPrefs.h
Normal file
57
launcher/InstanceCopyPrefs.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
//
|
||||||
|
// Created by marcelohdez on 10/22/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
struct InstanceCopyPrefs {
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool allTrue() const;
|
||||||
|
[[nodiscard]] QString getSelectedFiltersAsRegex() const;
|
||||||
|
[[nodiscard]] QString getSelectedFiltersAsRegex(const QStringList& additionalFilters) const;
|
||||||
|
// Getters
|
||||||
|
[[nodiscard]] bool isCopySavesEnabled() const;
|
||||||
|
[[nodiscard]] bool isKeepPlaytimeEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyGameOptionsEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyResourcePacksEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyShaderPacksEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyServersEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyModsEnabled() const;
|
||||||
|
[[nodiscard]] bool isCopyScreenshotsEnabled() const;
|
||||||
|
[[nodiscard]] bool isUseSymLinksEnabled() const;
|
||||||
|
[[nodiscard]] bool isLinkRecursivelyEnabled() const;
|
||||||
|
[[nodiscard]] bool isUseHardLinksEnabled() const;
|
||||||
|
[[nodiscard]] bool isDontLinkSavesEnabled() const;
|
||||||
|
[[nodiscard]] bool isUseCloneEnabled() const;
|
||||||
|
// Setters
|
||||||
|
void enableCopySaves(bool b);
|
||||||
|
void enableKeepPlaytime(bool b);
|
||||||
|
void enableCopyGameOptions(bool b);
|
||||||
|
void enableCopyResourcePacks(bool b);
|
||||||
|
void enableCopyShaderPacks(bool b);
|
||||||
|
void enableCopyServers(bool b);
|
||||||
|
void enableCopyMods(bool b);
|
||||||
|
void enableCopyScreenshots(bool b);
|
||||||
|
void enableUseSymLinks(bool b);
|
||||||
|
void enableLinkRecursively(bool b);
|
||||||
|
void enableUseHardLinks(bool b);
|
||||||
|
void enableDontLinkSaves(bool b);
|
||||||
|
void enableUseClone(bool b);
|
||||||
|
|
||||||
|
protected: // data
|
||||||
|
bool copySaves = true;
|
||||||
|
bool keepPlaytime = true;
|
||||||
|
bool copyGameOptions = true;
|
||||||
|
bool copyResourcePacks = true;
|
||||||
|
bool copyShaderPacks = true;
|
||||||
|
bool copyServers = true;
|
||||||
|
bool copyMods = true;
|
||||||
|
bool copyScreenshots = true;
|
||||||
|
bool useSymLinks = false;
|
||||||
|
bool linkRecursively = false;
|
||||||
|
bool useHardLinks = false;
|
||||||
|
bool dontLinkSaves = false;
|
||||||
|
bool useClone = false;
|
||||||
|
};
|
@ -1,19 +1,34 @@
|
|||||||
#include "InstanceCopyTask.h"
|
#include "InstanceCopyTask.h"
|
||||||
#include "settings/INISettingsObject.h"
|
#include <QDebug>
|
||||||
|
#include <QtConcurrentRun>
|
||||||
#include "FileSystem.h"
|
#include "FileSystem.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
#include "pathmatcher/RegexpMatcher.h"
|
#include "pathmatcher/RegexpMatcher.h"
|
||||||
#include <QtConcurrentRun>
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
|
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs)
|
||||||
{
|
{
|
||||||
m_origInstance = origInstance;
|
m_origInstance = origInstance;
|
||||||
m_keepPlaytime = keepPlaytime;
|
m_keepPlaytime = prefs.isKeepPlaytimeEnabled();
|
||||||
|
m_useLinks = prefs.isUseSymLinksEnabled();
|
||||||
|
m_linkRecursively = prefs.isLinkRecursivelyEnabled();
|
||||||
|
m_useHardLinks = prefs.isLinkRecursivelyEnabled() && prefs.isUseHardLinksEnabled();
|
||||||
|
m_copySaves = prefs.isLinkRecursivelyEnabled() && prefs.isDontLinkSavesEnabled() && prefs.isCopySavesEnabled();
|
||||||
|
m_useClone = prefs.isUseCloneEnabled();
|
||||||
|
|
||||||
if(!copySaves)
|
QString filters = prefs.getSelectedFiltersAsRegex();
|
||||||
{
|
if (m_useLinks || m_useHardLinks) {
|
||||||
|
if (!filters.isEmpty())
|
||||||
|
filters += "|";
|
||||||
|
filters += "instance.cfg";
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "CopyFilters:" << filters;
|
||||||
|
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
// Set regex filter:
|
||||||
// FIXME: get this from the original instance type...
|
// FIXME: get this from the original instance type...
|
||||||
auto matcherReal = new RegexpMatcher("[.]?minecraft/saves");
|
auto matcherReal = new RegexpMatcher(filters);
|
||||||
matcherReal->caseSensitive(false);
|
matcherReal->caseSensitive(false);
|
||||||
m_matcher.reset(matcherReal);
|
m_matcher.reset(matcherReal);
|
||||||
}
|
}
|
||||||
@ -23,10 +38,88 @@ void InstanceCopyTask::executeTask()
|
|||||||
{
|
{
|
||||||
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
setStatus(tr("Copying instance %1").arg(m_origInstance->name()));
|
||||||
|
|
||||||
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
auto copySaves = [&]() {
|
||||||
folderCopy.followSymlinks(false).blacklist(m_matcher.get());
|
QFileInfo mcDir(FS::PathCombine(m_stagingPath, "minecraft"));
|
||||||
|
QFileInfo dotMCDir(FS::PathCombine(m_stagingPath, ".minecraft"));
|
||||||
|
|
||||||
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), folderCopy);
|
QString staging_mc_dir;
|
||||||
|
if (mcDir.exists() && !dotMCDir.exists())
|
||||||
|
staging_mc_dir = mcDir.filePath();
|
||||||
|
else
|
||||||
|
staging_mc_dir = dotMCDir.filePath();
|
||||||
|
|
||||||
|
FS::copy savesCopy(FS::PathCombine(m_origInstance->gameRoot(), "saves"), FS::PathCombine(staging_mc_dir, "saves"));
|
||||||
|
savesCopy.followSymlinks(true);
|
||||||
|
|
||||||
|
return savesCopy();
|
||||||
|
};
|
||||||
|
|
||||||
|
m_copyFuture = QtConcurrent::run(QThreadPool::globalInstance(), [this, copySaves] {
|
||||||
|
if (m_useClone) {
|
||||||
|
FS::clone folderClone(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
|
folderClone.matcher(m_matcher.get());
|
||||||
|
|
||||||
|
return folderClone();
|
||||||
|
} else if (m_useLinks || m_useHardLinks) {
|
||||||
|
FS::create_link folderLink(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
|
int depth = m_linkRecursively ? -1 : 0; // we need to at least link the top level instead of the instance folder
|
||||||
|
folderLink.linkRecursively(true).setMaxDepth(depth).useHardLinks(m_useHardLinks).matcher(m_matcher.get());
|
||||||
|
|
||||||
|
bool there_were_errors = false;
|
||||||
|
|
||||||
|
if (!folderLink()) {
|
||||||
|
#if defined Q_OS_WIN32
|
||||||
|
if (!m_useHardLinks) {
|
||||||
|
qDebug() << "EXPECTED: Link failure, Windows requires permissions for symlinks";
|
||||||
|
|
||||||
|
qDebug() << "attempting to run with privelage";
|
||||||
|
|
||||||
|
QEventLoop loop;
|
||||||
|
bool got_priv_results = false;
|
||||||
|
|
||||||
|
connect(&folderLink, &FS::create_link::finishedPrivileged, this, [&](bool gotResults) {
|
||||||
|
if (!gotResults) {
|
||||||
|
qDebug() << "Privileged run exited without results!";
|
||||||
|
}
|
||||||
|
got_priv_results = gotResults;
|
||||||
|
loop.quit();
|
||||||
|
});
|
||||||
|
folderLink.runPrivileged();
|
||||||
|
|
||||||
|
loop.exec(); // wait for the finished signal
|
||||||
|
|
||||||
|
for (auto result : folderLink.getResults()) {
|
||||||
|
if (result.err_value != 0) {
|
||||||
|
there_were_errors = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_copySaves) {
|
||||||
|
there_were_errors |= !copySaves();
|
||||||
|
}
|
||||||
|
|
||||||
|
return got_priv_results && !there_were_errors;
|
||||||
|
} else {
|
||||||
|
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
qDebug() << "Link Failed!" << folderLink.getOSError().value() << folderLink.getOSError().message().c_str();
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_copySaves) {
|
||||||
|
there_were_errors |= !copySaves();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !there_were_errors;
|
||||||
|
} else {
|
||||||
|
FS::copy folderCopy(m_origInstance->instanceRoot(), m_stagingPath);
|
||||||
|
folderCopy.followSymlinks(false).matcher(m_matcher.get());
|
||||||
|
|
||||||
|
return folderCopy();
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::finished, this, &InstanceCopyTask::copyFinished);
|
||||||
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
|
connect(&m_copyFutureWatcher, &QFutureWatcher<bool>::canceled, this, &InstanceCopyTask::copyAborted);
|
||||||
m_copyFutureWatcher.setFuture(m_copyFuture);
|
m_copyFutureWatcher.setFuture(m_copyFuture);
|
||||||
@ -35,20 +128,40 @@ void InstanceCopyTask::executeTask()
|
|||||||
void InstanceCopyTask::copyFinished()
|
void InstanceCopyTask::copyFinished()
|
||||||
{
|
{
|
||||||
auto successful = m_copyFuture.result();
|
auto successful = m_copyFuture.result();
|
||||||
if(!successful)
|
if (!successful) {
|
||||||
{
|
|
||||||
emitFailed(tr("Instance folder copy failed."));
|
emitFailed(tr("Instance folder copy failed."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: shouldn't this be able to report errors?
|
// FIXME: shouldn't this be able to report errors?
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
||||||
|
|
||||||
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
|
||||||
inst->setName(m_instName);
|
inst->setName(name());
|
||||||
inst->setIconKey(m_instIcon);
|
inst->setIconKey(m_instIcon);
|
||||||
if (!m_keepPlaytime) {
|
if (!m_keepPlaytime) {
|
||||||
inst->resetTimePlayed();
|
inst->resetTimePlayed();
|
||||||
}
|
}
|
||||||
|
if (m_useLinks)
|
||||||
|
inst->addLinkedInstanceId(m_origInstance->id());
|
||||||
|
if (m_useLinks) {
|
||||||
|
auto allowed_symlinks_file = QFileInfo(FS::PathCombine(inst->gameRoot(), "allowed_symlinks.txt"));
|
||||||
|
|
||||||
|
QByteArray allowed_symlinks;
|
||||||
|
if (allowed_symlinks_file.exists()) {
|
||||||
|
allowed_symlinks.append(FS::read(allowed_symlinks_file.filePath()));
|
||||||
|
if (allowed_symlinks.right(1) != "\n")
|
||||||
|
allowed_symlinks.append("\n"); // we want to be on a new line
|
||||||
|
}
|
||||||
|
allowed_symlinks.append(m_origInstance->gameRoot().toUtf8());
|
||||||
|
allowed_symlinks.append("\n");
|
||||||
|
if (allowed_symlinks_file.isSymLink())
|
||||||
|
FS::deletePath(allowed_symlinks_file
|
||||||
|
.filePath()); // we dont want to modify the original. also make sure the resulting file is not itself a link.
|
||||||
|
|
||||||
|
FS::write(allowed_symlinks_file.filePath(), allowed_symlinks);
|
||||||
|
}
|
||||||
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include "settings/SettingsObject.h"
|
#include <QUrl>
|
||||||
#include "BaseVersion.h"
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
#include "BaseVersion.h"
|
||||||
|
#include "InstanceCopyPrefs.h"
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
#include "net/NetJob.h"
|
||||||
|
#include "settings/SettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
class InstanceCopyTask : public InstanceTask
|
class InstanceCopyTask : public InstanceTask
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
|
explicit InstanceCopyTask(InstancePtr origInstance, const InstanceCopyPrefs& prefs);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
@ -22,10 +23,16 @@ protected:
|
|||||||
void copyFinished();
|
void copyFinished();
|
||||||
void copyAborted();
|
void copyAborted();
|
||||||
|
|
||||||
private: /* data */
|
private:
|
||||||
|
/* data */
|
||||||
InstancePtr m_origInstance;
|
InstancePtr m_origInstance;
|
||||||
QFuture<bool> m_copyFuture;
|
QFuture<bool> m_copyFuture;
|
||||||
QFutureWatcher<bool> m_copyFutureWatcher;
|
QFutureWatcher<bool> m_copyFutureWatcher;
|
||||||
std::unique_ptr<IPathMatcher> m_matcher;
|
std::unique_ptr<IPathMatcher> m_matcher;
|
||||||
bool m_keepPlaytime;
|
bool m_keepPlaytime;
|
||||||
|
bool m_useLinks = false;
|
||||||
|
bool m_useHardLinks = false;
|
||||||
|
bool m_copySaves = false;
|
||||||
|
bool m_linkRecursively = false;
|
||||||
|
bool m_useClone = false;
|
||||||
};
|
};
|
||||||
|
@ -1,29 +1,60 @@
|
|||||||
#include "InstanceCreationTask.h"
|
#include "InstanceCreationTask.h"
|
||||||
#include "settings/INISettingsObject.h"
|
|
||||||
#include "FileSystem.h"
|
|
||||||
|
|
||||||
//FIXME: remove this
|
#include <QDebug>
|
||||||
#include "minecraft/MinecraftInstance.h"
|
#include <QFile>
|
||||||
#include "minecraft/PackProfile.h"
|
|
||||||
|
|
||||||
InstanceCreationTask::InstanceCreationTask(BaseVersionPtr version)
|
InstanceCreationTask::InstanceCreationTask() = default;
|
||||||
{
|
|
||||||
m_version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceCreationTask::executeTask()
|
void InstanceCreationTask::executeTask()
|
||||||
{
|
{
|
||||||
setStatus(tr("Creating instance from version %1").arg(m_version->name()));
|
setAbortable(true);
|
||||||
{
|
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(m_stagingPath, "instance.cfg"));
|
if (updateInstance()) {
|
||||||
instanceSettings->suspendSave();
|
|
||||||
MinecraftInstance inst(m_globalSettings, instanceSettings, m_stagingPath);
|
|
||||||
auto components = inst.getPackProfile();
|
|
||||||
components->buildingFromScratch();
|
|
||||||
components->setComponentVersion("net.minecraft", m_version->descriptor(), true);
|
|
||||||
inst.setName(m_instName);
|
|
||||||
inst.setIconKey(m_instIcon);
|
|
||||||
instanceSettings->resumeSave();
|
|
||||||
}
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the user aborted in the update stage.
|
||||||
|
if (m_abort) {
|
||||||
|
emitAborted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!createInstance()) {
|
||||||
|
if (m_abort)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qWarning() << "Instance creation failed!";
|
||||||
|
if (!m_error_message.isEmpty()) {
|
||||||
|
qWarning() << "Reason: " << m_error_message;
|
||||||
|
emitFailed(tr("Error while creating new instance:\n%1").arg(m_error_message));
|
||||||
|
} else {
|
||||||
|
emitFailed(tr("Error while creating new instance."));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is set, it means we're updating an instance. So, we now need to remove the
|
||||||
|
// files scheduled to, and we'd better not let the user abort in the middle of it, since it'd
|
||||||
|
// put the instance in an invalid state.
|
||||||
|
if (shouldOverride()) {
|
||||||
|
setAbortable(false);
|
||||||
|
setStatus(tr("Removing old conflicting files..."));
|
||||||
|
qDebug() << "Removing old files";
|
||||||
|
|
||||||
|
for (auto path : m_files_to_remove) {
|
||||||
|
if (!QFile::exists(path))
|
||||||
|
continue;
|
||||||
|
qDebug() << "Removing" << path;
|
||||||
|
if (!QFile::remove(path)) {
|
||||||
|
qCritical() << "Couldn't remove the old conflicting files.";
|
||||||
|
emitFailed(tr("Failed to remove old conflicting files."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitSucceeded();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,46 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "net/NetJob.h"
|
|
||||||
#include <QUrl>
|
|
||||||
#include "settings/SettingsObject.h"
|
|
||||||
#include "BaseVersion.h"
|
#include "BaseVersion.h"
|
||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
|
||||||
class InstanceCreationTask : public InstanceTask
|
class InstanceCreationTask : public InstanceTask {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceCreationTask(BaseVersionPtr version);
|
InstanceCreationTask();
|
||||||
|
virtual ~InstanceCreationTask() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
void executeTask() final override;
|
||||||
virtual void executeTask() override;
|
|
||||||
|
|
||||||
private: /* data */
|
/**
|
||||||
BaseVersionPtr m_version;
|
* Tries to update an already existing instance.
|
||||||
|
*
|
||||||
|
* This can be implemented by subclasses to provide a way of updating an already existing
|
||||||
|
* instance, according to that implementation's concept of 'identity' (i.e. instances that
|
||||||
|
* are updates / downgrades of one another).
|
||||||
|
*
|
||||||
|
* If this returns true, createInstance() will not run, so you should do all update steps in here.
|
||||||
|
* Otherwise, createInstance() is run as normal.
|
||||||
|
*/
|
||||||
|
virtual bool updateInstance() { return false; };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* Returns whether the instance creation was successful (true) or not (false).
|
||||||
|
*/
|
||||||
|
virtual bool createInstance() { return false; };
|
||||||
|
|
||||||
|
QString getError() const { return m_error_message; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setError(const QString& message) { m_error_message = message; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool m_abort = false;
|
||||||
|
|
||||||
|
QStringList m_files_to_remove;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_error_message;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,25 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2022 flowln <flowlnlnln@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,54 +35,74 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "InstanceImportTask.h"
|
#include "InstanceImportTask.h"
|
||||||
#include "BaseInstance.h"
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
|
#include "FileSystem.h"
|
||||||
#include "MMCZip.h"
|
#include "MMCZip.h"
|
||||||
#include "NullInstance.h"
|
#include "NullInstance.h"
|
||||||
#include "settings/INISettingsObject.h"
|
|
||||||
#include "icons/IconUtils.h"
|
|
||||||
#include <QtConcurrentRun>
|
|
||||||
|
|
||||||
// FIXME: this does not belong here, it's Minecraft/Flame specific
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
|
||||||
#include "minecraft/PackProfile.h"
|
|
||||||
#include "modplatform/flame/FileResolvingTask.h"
|
|
||||||
#include "modplatform/flame/PackManifest.h"
|
|
||||||
#include "Json.h"
|
|
||||||
#include <quazip/quazipdir.h>
|
|
||||||
#include "modplatform/technic/TechnicPackProcessor.h"
|
|
||||||
|
|
||||||
|
#include "QObjectPtr.h"
|
||||||
#include "icons/IconList.h"
|
#include "icons/IconList.h"
|
||||||
#include "Application.h"
|
#include "icons/IconUtils.h"
|
||||||
|
|
||||||
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl)
|
#include "modplatform/technic/TechnicPackProcessor.h"
|
||||||
|
#include "modplatform/modrinth/ModrinthInstanceCreationTask.h"
|
||||||
|
#include "modplatform/flame/FlameInstanceCreationTask.h"
|
||||||
|
|
||||||
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
|
#include <QtConcurrentRun>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <quazip/quazipdir.h>
|
||||||
|
|
||||||
|
InstanceImportTask::InstanceImportTask(const QUrl sourceUrl, QWidget* parent, QMap<QString, QString>&& extra_info)
|
||||||
|
: m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool InstanceImportTask::abort()
|
||||||
{
|
{
|
||||||
m_sourceUrl = sourceUrl;
|
if (!canAbort())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (m_filesNetJob)
|
||||||
|
m_filesNetJob->abort();
|
||||||
|
if (m_extractFuture.isRunning()) {
|
||||||
|
// NOTE: The tasks created by QtConcurrent::run() can't actually get cancelled,
|
||||||
|
// but we can use this call to check the state when the extraction finishes.
|
||||||
|
m_extractFuture.cancel();
|
||||||
|
m_extractFuture.waitForFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::executeTask()
|
void InstanceImportTask::executeTask()
|
||||||
{
|
{
|
||||||
if (m_sourceUrl.isLocalFile())
|
setAbortable(true);
|
||||||
{
|
|
||||||
|
if (m_sourceUrl.isLocalFile()) {
|
||||||
m_archivePath = m_sourceUrl.toLocalFile();
|
m_archivePath = m_sourceUrl.toLocalFile();
|
||||||
processZipPack();
|
processZipPack();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
setStatus(tr("Downloading modpack:\n%1").arg(m_sourceUrl.toString()));
|
||||||
m_downloadRequired = true;
|
m_downloadRequired = true;
|
||||||
|
|
||||||
const QString path = m_sourceUrl.host() + '/' + m_sourceUrl.path();
|
const QString path(m_sourceUrl.host() + '/' + m_sourceUrl.path());
|
||||||
|
|
||||||
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
auto entry = APPLICATION->metacache()->resolveEntry("general", path);
|
||||||
entry->setStale(true);
|
entry->setStale(true);
|
||||||
m_filesNetJob = new NetJob(tr("Modpack download"), APPLICATION->network());
|
|
||||||
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
|
||||||
m_archivePath = entry->getFullPath();
|
m_archivePath = entry->getFullPath();
|
||||||
auto job = m_filesNetJob.get();
|
|
||||||
connect(job, &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
m_filesNetJob.reset(new NetJob(tr("Modpack download"), APPLICATION->network()));
|
||||||
connect(job, &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
m_filesNetJob->addNetAction(Net::Download::makeCached(m_sourceUrl, entry));
|
||||||
connect(job, &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::succeeded, this, &InstanceImportTask::downloadSucceeded);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::progress, this, &InstanceImportTask::downloadProgressChanged);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::failed, this, &InstanceImportTask::downloadFailed);
|
||||||
|
connect(m_filesNetJob.get(), &NetJob::aborted, this, &InstanceImportTask::downloadAborted);
|
||||||
|
|
||||||
m_filesNetJob->start();
|
m_filesNetJob->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,7 +121,13 @@ void InstanceImportTask::downloadFailed(QString reason)
|
|||||||
|
|
||||||
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
void InstanceImportTask::downloadProgressChanged(qint64 current, qint64 total)
|
||||||
{
|
{
|
||||||
setProgress(current / 2, total);
|
setProgress(current, total);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::downloadAborted()
|
||||||
|
{
|
||||||
|
emitAborted();
|
||||||
|
m_filesNetJob.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processZipPack()
|
void InstanceImportTask::processZipPack()
|
||||||
@ -97,17 +144,20 @@ void InstanceImportTask::processZipPack()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList blacklist = {"instance.cfg", "manifest.json"};
|
QuaZipDir packZipDir(m_packZip.get());
|
||||||
QString mmcFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg");
|
|
||||||
bool technicFound = QuaZipDir(m_packZip.get()).exists("/bin/modpack.jar") || QuaZipDir(m_packZip.get()).exists("/bin/version.json");
|
// https://docs.modrinth.com/docs/modpacks/format_definition/#storage
|
||||||
QString flameFound = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json");
|
bool modrinthFound = packZipDir.exists("/modrinth.index.json");
|
||||||
|
bool technicFound = packZipDir.exists("/bin/modpack.jar") || packZipDir.exists("/bin/version.json");
|
||||||
QString root;
|
QString root;
|
||||||
if(!mmcFound.isNull())
|
|
||||||
|
// NOTE: Prioritize modpack platforms that aren't searched for recursively.
|
||||||
|
// Especially Flame has a very common filename for its manifest, which may appear inside overrides for example
|
||||||
|
if(modrinthFound)
|
||||||
{
|
{
|
||||||
// process as MultiMC instance/pack
|
// process as Modrinth pack
|
||||||
qDebug() << "MultiMC:" << mmcFound;
|
qDebug() << "Modrinth:" << modrinthFound;
|
||||||
root = mmcFound;
|
m_modpackType = ModpackType::Modrinth;
|
||||||
m_modpackType = ModpackType::MultiMC;
|
|
||||||
}
|
}
|
||||||
else if (technicFound)
|
else if (technicFound)
|
||||||
{
|
{
|
||||||
@ -117,13 +167,22 @@ void InstanceImportTask::processZipPack()
|
|||||||
extractDir.cd(".minecraft");
|
extractDir.cd(".minecraft");
|
||||||
m_modpackType = ModpackType::Technic;
|
m_modpackType = ModpackType::Technic;
|
||||||
}
|
}
|
||||||
else if(!flameFound.isNull())
|
else
|
||||||
{
|
{
|
||||||
|
QStringList paths_to_ignore { "overrides/" };
|
||||||
|
|
||||||
|
if (QString mmcRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "instance.cfg", paths_to_ignore); !mmcRoot.isNull()) {
|
||||||
|
// process as MultiMC instance/pack
|
||||||
|
qDebug() << "MultiMC:" << mmcRoot;
|
||||||
|
root = mmcRoot;
|
||||||
|
m_modpackType = ModpackType::MultiMC;
|
||||||
|
} else if (QString flameRoot = MMCZip::findFolderOfFileInZip(m_packZip.get(), "manifest.json", paths_to_ignore); !flameRoot.isNull()) {
|
||||||
// process as Flame pack
|
// process as Flame pack
|
||||||
qDebug() << "Flame:" << flameFound;
|
qDebug() << "Flame:" << flameRoot;
|
||||||
root = flameFound;
|
root = flameRoot;
|
||||||
m_modpackType = ModpackType::Flame;
|
m_modpackType = ModpackType::Flame;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if(m_modpackType == ModpackType::Unknown)
|
if(m_modpackType == ModpackType::Unknown)
|
||||||
{
|
{
|
||||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||||
@ -133,18 +192,20 @@ void InstanceImportTask::processZipPack()
|
|||||||
// make sure we extract just the pack
|
// make sure we extract just the pack
|
||||||
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractSubDir, m_packZip.get(), root, extractDir.absolutePath());
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &InstanceImportTask::extractFinished);
|
||||||
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &InstanceImportTask::extractAborted);
|
|
||||||
m_extractFutureWatcher.setFuture(m_extractFuture);
|
m_extractFutureWatcher.setFuture(m_extractFuture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::extractFinished()
|
void InstanceImportTask::extractFinished()
|
||||||
{
|
{
|
||||||
m_packZip.reset();
|
m_packZip.reset();
|
||||||
if (!m_extractFuture.result())
|
|
||||||
{
|
if (m_extractFuture.isCanceled())
|
||||||
|
return;
|
||||||
|
if (!m_extractFuture.result().has_value()) {
|
||||||
emitFailed(tr("Failed to extract modpack"));
|
emitFailed(tr("Failed to extract modpack"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDir extractDir(m_stagingPath);
|
QDir extractDir(m_stagingPath);
|
||||||
|
|
||||||
qDebug() << "Fixing permissions for extracted pack files...";
|
qDebug() << "Fixing permissions for extracted pack files...";
|
||||||
@ -180,240 +241,75 @@ void InstanceImportTask::extractFinished()
|
|||||||
|
|
||||||
switch(m_modpackType)
|
switch(m_modpackType)
|
||||||
{
|
{
|
||||||
case ModpackType::Flame:
|
|
||||||
processFlame();
|
|
||||||
return;
|
|
||||||
case ModpackType::MultiMC:
|
case ModpackType::MultiMC:
|
||||||
processMultiMC();
|
processMultiMC();
|
||||||
return;
|
return;
|
||||||
case ModpackType::Technic:
|
case ModpackType::Technic:
|
||||||
processTechnic();
|
processTechnic();
|
||||||
return;
|
return;
|
||||||
|
case ModpackType::Flame:
|
||||||
|
processFlame();
|
||||||
|
return;
|
||||||
|
case ModpackType::Modrinth:
|
||||||
|
processModrinth();
|
||||||
|
return;
|
||||||
case ModpackType::Unknown:
|
case ModpackType::Unknown:
|
||||||
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
emitFailed(tr("Archive does not contain a recognized modpack type."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::extractAborted()
|
|
||||||
{
|
|
||||||
emitFailed(tr("Instance import has been aborted."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InstanceImportTask::processFlame()
|
void InstanceImportTask::processFlame()
|
||||||
{
|
{
|
||||||
const static QMap<QString,QString> forgemap = {
|
shared_qobject_ptr<FlameCreationTask> inst_creation_task = nullptr;
|
||||||
{"1.2.5", "3.4.9.171"},
|
if (!m_extra_info.isEmpty()) {
|
||||||
{"1.4.2", "6.0.1.355"},
|
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||||
{"1.4.7", "6.6.2.534"},
|
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||||
{"1.5.2", "7.8.1.737"}
|
auto pack_id = pack_id_it.value();
|
||||||
};
|
|
||||||
Flame::Manifest pack;
|
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
||||||
try
|
Q_ASSERT(pack_version_id_it != m_extra_info.constEnd());
|
||||||
{
|
auto pack_version_id = pack_version_id_it.value();
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "manifest.json");
|
|
||||||
Flame::loadManifest(pack, configPath);
|
QString original_instance_id;
|
||||||
QFile::remove(configPath);
|
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
||||||
}
|
if (original_instance_id_it != m_extra_info.constEnd())
|
||||||
catch (const JSONValidationError &e)
|
original_instance_id = original_instance_id_it.value();
|
||||||
{
|
|
||||||
emitFailed(tr("Could not understand pack manifest:\n") + e.cause());
|
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||||
return;
|
} else {
|
||||||
}
|
// FIXME: Find a way to get IDs in directly imported ZIPs
|
||||||
if(!pack.overrides.isEmpty())
|
inst_creation_task = makeShared<FlameCreationTask>(m_stagingPath, m_globalSettings, m_parent, QString(), QString());
|
||||||
{
|
|
||||||
QString overridePath = FS::PathCombine(m_stagingPath, pack.overrides);
|
|
||||||
if (QFile::exists(overridePath))
|
|
||||||
{
|
|
||||||
QString mcPath = FS::PathCombine(m_stagingPath, "minecraft");
|
|
||||||
if (!QFile::rename(overridePath, mcPath))
|
|
||||||
{
|
|
||||||
emitFailed(tr("Could not rename the overrides folder:\n") + pack.overrides);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logWarning(tr("The specified overrides folder (%1) is missing. Maybe the modpack was already used before?").arg(pack.overrides));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString forgeVersion;
|
inst_creation_task->setName(*this);
|
||||||
QString fabricVersion;
|
inst_creation_task->setIcon(m_instIcon);
|
||||||
for(auto &loader: pack.minecraft.modLoaders)
|
inst_creation_task->setGroup(m_instGroup);
|
||||||
{
|
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||||
auto id = loader.id;
|
|
||||||
if(id.startsWith("forge-"))
|
|
||||||
{
|
|
||||||
id.remove("forge-");
|
|
||||||
forgeVersion = id;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(id.startsWith("fabric-"))
|
|
||||||
{
|
|
||||||
id.remove("fabric-");
|
|
||||||
fabricVersion = id;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
logWarning(tr("Unknown mod loader in manifest: %1").arg(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg");
|
connect(inst_creation_task.get(), &Task::succeeded, this, [this, inst_creation_task] {
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(configPath);
|
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||||
MinecraftInstance instance(m_globalSettings, instanceSettings, m_stagingPath);
|
|
||||||
auto mcVersion = pack.minecraft.version;
|
|
||||||
// Hack to correct some 'special sauce'...
|
|
||||||
if(mcVersion.endsWith('.'))
|
|
||||||
{
|
|
||||||
mcVersion.remove(QRegExp("[.]+$"));
|
|
||||||
logWarning(tr("Mysterious trailing dots removed from Minecraft version while importing pack."));
|
|
||||||
}
|
|
||||||
auto components = instance.getPackProfile();
|
|
||||||
components->buildingFromScratch();
|
|
||||||
components->setComponentVersion("net.minecraft", mcVersion, true);
|
|
||||||
if(!forgeVersion.isEmpty())
|
|
||||||
{
|
|
||||||
// FIXME: dirty, nasty, hack. Proper solution requires dependency resolution and knowledge of the metadata.
|
|
||||||
if(forgeVersion == "recommended")
|
|
||||||
{
|
|
||||||
if(forgemap.contains(mcVersion))
|
|
||||||
{
|
|
||||||
forgeVersion = forgemap[mcVersion];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logWarning(tr("Could not map recommended forge version for Minecraft %1").arg(mcVersion));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
components->setComponentVersion("net.minecraftforge", forgeVersion);
|
|
||||||
}
|
|
||||||
if(!fabricVersion.isEmpty())
|
|
||||||
{
|
|
||||||
components->setComponentVersion("net.fabricmc.fabric-loader", fabricVersion);
|
|
||||||
}
|
|
||||||
if (m_instIcon != "default")
|
|
||||||
{
|
|
||||||
instance.setIconKey(m_instIcon);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(pack.name.contains("Direwolf20"))
|
|
||||||
{
|
|
||||||
instance.setIconKey("steve");
|
|
||||||
}
|
|
||||||
else if(pack.name.contains("FTB") || pack.name.contains("Feed The Beast"))
|
|
||||||
{
|
|
||||||
instance.setIconKey("ftb_logo");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// default to something other than the MultiMC default to distinguish these
|
|
||||||
instance.setIconKey("flame");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QString jarmodsPath = FS::PathCombine(m_stagingPath, "minecraft", "jarmods");
|
|
||||||
QFileInfo jarmodsInfo(jarmodsPath);
|
|
||||||
if(jarmodsInfo.isDir())
|
|
||||||
{
|
|
||||||
// install all the jar mods
|
|
||||||
qDebug() << "Found jarmods:";
|
|
||||||
QDir jarmodsDir(jarmodsPath);
|
|
||||||
QStringList jarMods;
|
|
||||||
for (auto info: jarmodsDir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files))
|
|
||||||
{
|
|
||||||
qDebug() << info.fileName();
|
|
||||||
jarMods.push_back(info.absoluteFilePath());
|
|
||||||
}
|
|
||||||
auto profile = instance.getPackProfile();
|
|
||||||
profile->installJarMods(jarMods);
|
|
||||||
// nuke the original files
|
|
||||||
FS::deletePath(jarmodsPath);
|
|
||||||
}
|
|
||||||
instance.setName(m_instName);
|
|
||||||
m_modIdResolver = new Flame::FileResolvingTask(APPLICATION->network(), pack);
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::succeeded, [&]()
|
|
||||||
{
|
|
||||||
auto results = m_modIdResolver->getResults();
|
|
||||||
m_filesNetJob = new NetJob(tr("Mod download"), APPLICATION->network());
|
|
||||||
for(auto result: results.files)
|
|
||||||
{
|
|
||||||
QString filename = result.fileName;
|
|
||||||
if(!result.required)
|
|
||||||
{
|
|
||||||
filename += ".disabled";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto relpath = FS::PathCombine("minecraft", result.targetFolder, filename);
|
|
||||||
auto path = FS::PathCombine(m_stagingPath , relpath);
|
|
||||||
|
|
||||||
switch(result.type)
|
|
||||||
{
|
|
||||||
case Flame::File::Type::Folder:
|
|
||||||
{
|
|
||||||
logWarning(tr("This 'Folder' may need extracting: %1").arg(relpath));
|
|
||||||
// fall-through intentional, we treat these as plain old mods and dump them wherever.
|
|
||||||
}
|
|
||||||
case Flame::File::Type::SingleFile:
|
|
||||||
case Flame::File::Type::Mod:
|
|
||||||
{
|
|
||||||
qDebug() << "Will download" << result.url << "to" << path;
|
|
||||||
auto dl = Net::Download::makeFile(result.url, path);
|
|
||||||
m_filesNetJob->addNetAction(dl);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Flame::File::Type::Modpack:
|
|
||||||
logWarning(tr("Nesting modpacks in modpacks is not implemented, nothing was downloaded: %1").arg(relpath));
|
|
||||||
break;
|
|
||||||
case Flame::File::Type::Cmod2:
|
|
||||||
case Flame::File::Type::Ctoc:
|
|
||||||
case Flame::File::Type::Unknown:
|
|
||||||
logWarning(tr("Unrecognized/unhandled PackageType for: %1").arg(relpath));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_modIdResolver.reset();
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::succeeded, this, [&]()
|
|
||||||
{
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
|
||||||
);
|
|
||||||
connect(m_filesNetJob.get(), &NetJob::failed, [&](QString reason)
|
|
||||||
{
|
|
||||||
m_filesNetJob.reset();
|
|
||||||
emitFailed(reason);
|
|
||||||
});
|
});
|
||||||
connect(m_filesNetJob.get(), &NetJob::progress, [&](qint64 current, qint64 total)
|
connect(inst_creation_task.get(), &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||||
{
|
connect(inst_creation_task.get(), &Task::progress, this, &InstanceImportTask::setProgress);
|
||||||
setProgress(current, total);
|
connect(inst_creation_task.get(), &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||||
});
|
connect(inst_creation_task.get(), &Task::status, this, &InstanceImportTask::setStatus);
|
||||||
setStatus(tr("Downloading mods..."));
|
connect(inst_creation_task.get(), &Task::details, this, &InstanceImportTask::setDetails);
|
||||||
m_filesNetJob->start();
|
|
||||||
}
|
connect(this, &Task::aborted, inst_creation_task.get(), &InstanceCreationTask::abort);
|
||||||
);
|
connect(inst_creation_task.get(), &Task::aborted, this, &Task::abort);
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::failed, [&](QString reason)
|
connect(inst_creation_task.get(), &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||||
{
|
|
||||||
m_modIdResolver.reset();
|
inst_creation_task->start();
|
||||||
emitFailed(tr("Unable to resolve mod IDs:\n") + reason);
|
|
||||||
});
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::progress, [&](qint64 current, qint64 total)
|
|
||||||
{
|
|
||||||
setProgress(current, total);
|
|
||||||
});
|
|
||||||
connect(m_modIdResolver.get(), &Flame::FileResolvingTask::status, [&](QString status)
|
|
||||||
{
|
|
||||||
setStatus(status);
|
|
||||||
});
|
|
||||||
m_modIdResolver->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processTechnic()
|
void InstanceImportTask::processTechnic()
|
||||||
{
|
{
|
||||||
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor = new Technic::TechnicPackProcessor();
|
shared_qobject_ptr<Technic::TechnicPackProcessor> packProcessor{ new Technic::TechnicPackProcessor };
|
||||||
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
|
connect(packProcessor.get(), &Technic::TechnicPackProcessor::succeeded, this, &InstanceImportTask::emitSucceeded);
|
||||||
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
|
connect(packProcessor.get(), &Technic::TechnicPackProcessor::failed, this, &InstanceImportTask::emitFailed);
|
||||||
packProcessor->run(m_globalSettings, m_instName, m_instIcon, m_stagingPath);
|
packProcessor->run(m_globalSettings, name(), m_instIcon, m_stagingPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceImportTask::processMultiMC()
|
void InstanceImportTask::processMultiMC()
|
||||||
@ -427,24 +323,19 @@ void InstanceImportTask::processMultiMC()
|
|||||||
instance.resetTimePlayed();
|
instance.resetTimePlayed();
|
||||||
|
|
||||||
// set a new nice name
|
// set a new nice name
|
||||||
instance.setName(m_instName);
|
instance.setName(name());
|
||||||
|
|
||||||
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
// if the icon was specified by user, use that. otherwise pull icon from the pack
|
||||||
if (m_instIcon != "default")
|
if (m_instIcon != "default") {
|
||||||
{
|
|
||||||
instance.setIconKey(m_instIcon);
|
instance.setIconKey(m_instIcon);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
m_instIcon = instance.iconKey();
|
m_instIcon = instance.iconKey();
|
||||||
|
|
||||||
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
|
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
|
||||||
if (!importIconPath.isNull() && QFile::exists(importIconPath))
|
if (!importIconPath.isNull() && QFile::exists(importIconPath)) {
|
||||||
{
|
|
||||||
// import icon
|
// import icon
|
||||||
auto iconList = APPLICATION->icons();
|
auto iconList = APPLICATION->icons();
|
||||||
if (iconList->iconFileExists(m_instIcon))
|
if (iconList->iconFileExists(m_instIcon)) {
|
||||||
{
|
|
||||||
iconList->deleteIcon(m_instIcon);
|
iconList->deleteIcon(m_instIcon);
|
||||||
}
|
}
|
||||||
iconList->installIcons({ importIconPath });
|
iconList->installIcons({ importIconPath });
|
||||||
@ -452,3 +343,56 @@ void InstanceImportTask::processMultiMC()
|
|||||||
}
|
}
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceImportTask::processModrinth()
|
||||||
|
{
|
||||||
|
ModrinthCreationTask* inst_creation_task = nullptr;
|
||||||
|
if (!m_extra_info.isEmpty()) {
|
||||||
|
auto pack_id_it = m_extra_info.constFind("pack_id");
|
||||||
|
Q_ASSERT(pack_id_it != m_extra_info.constEnd());
|
||||||
|
auto pack_id = pack_id_it.value();
|
||||||
|
|
||||||
|
QString pack_version_id;
|
||||||
|
auto pack_version_id_it = m_extra_info.constFind("pack_version_id");
|
||||||
|
if (pack_version_id_it != m_extra_info.constEnd())
|
||||||
|
pack_version_id = pack_version_id_it.value();
|
||||||
|
|
||||||
|
QString original_instance_id;
|
||||||
|
auto original_instance_id_it = m_extra_info.constFind("original_instance_id");
|
||||||
|
if (original_instance_id_it != m_extra_info.constEnd())
|
||||||
|
original_instance_id = original_instance_id_it.value();
|
||||||
|
|
||||||
|
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id);
|
||||||
|
} else {
|
||||||
|
QString pack_id;
|
||||||
|
if (!m_sourceUrl.isEmpty()) {
|
||||||
|
QRegularExpression regex(R"(data\/([^\/]*)\/versions)");
|
||||||
|
pack_id = regex.match(m_sourceUrl.toString()).captured(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Find a way to get the ID in directly imported ZIPs
|
||||||
|
inst_creation_task = new ModrinthCreationTask(m_stagingPath, m_globalSettings, m_parent, pack_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
inst_creation_task->setName(*this);
|
||||||
|
inst_creation_task->setIcon(m_instIcon);
|
||||||
|
inst_creation_task->setGroup(m_instGroup);
|
||||||
|
inst_creation_task->setConfirmUpdate(shouldConfirmUpdate());
|
||||||
|
|
||||||
|
connect(inst_creation_task, &Task::succeeded, this, [this, inst_creation_task] {
|
||||||
|
setOverride(inst_creation_task->shouldOverride(), inst_creation_task->originalInstanceID());
|
||||||
|
emitSucceeded();
|
||||||
|
});
|
||||||
|
connect(inst_creation_task, &Task::failed, this, &InstanceImportTask::emitFailed);
|
||||||
|
connect(inst_creation_task, &Task::progress, this, &InstanceImportTask::setProgress);
|
||||||
|
connect(inst_creation_task, &Task::stepProgress, this, &InstanceImportTask::propogateStepProgress);
|
||||||
|
connect(inst_creation_task, &Task::status, this, &InstanceImportTask::setStatus);
|
||||||
|
connect(inst_creation_task, &Task::details, this, &InstanceImportTask::setDetails);
|
||||||
|
connect(inst_creation_task, &Task::finished, inst_creation_task, &InstanceCreationTask::deleteLater);
|
||||||
|
|
||||||
|
connect(this, &Task::aborted, inst_creation_task, &InstanceCreationTask::abort);
|
||||||
|
connect(inst_creation_task, &Task::aborted, this, &Task::abort);
|
||||||
|
connect(inst_creation_task, &Task::abortStatusChanged, this, &Task::setAbortable);
|
||||||
|
|
||||||
|
inst_creation_task->start();
|
||||||
|
}
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -22,8 +42,9 @@
|
|||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
#include "QObjectPtr.h"
|
#include "QObjectPtr.h"
|
||||||
|
#include "modplatform/flame/PackManifest.h"
|
||||||
|
|
||||||
#include <nonstd/optional>
|
#include <optional>
|
||||||
|
|
||||||
class QuaZip;
|
class QuaZip;
|
||||||
namespace Flame
|
namespace Flame
|
||||||
@ -35,7 +56,13 @@ class InstanceImportTask : public InstanceTask
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceImportTask(const QUrl sourceUrl);
|
explicit InstanceImportTask(const QUrl sourceUrl, QWidget* parent = nullptr, QMap<QString, QString>&& extra_info = {});
|
||||||
|
|
||||||
|
bool abort() override;
|
||||||
|
const QVector<Flame::File> &getBlockedFiles() const
|
||||||
|
{
|
||||||
|
return m_blockedMods;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//! Entry point for tasks.
|
//! Entry point for tasks.
|
||||||
@ -44,15 +71,16 @@ protected:
|
|||||||
private:
|
private:
|
||||||
void processZipPack();
|
void processZipPack();
|
||||||
void processMultiMC();
|
void processMultiMC();
|
||||||
void processFlame();
|
|
||||||
void processTechnic();
|
void processTechnic();
|
||||||
|
void processFlame();
|
||||||
|
void processModrinth();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void downloadSucceeded();
|
void downloadSucceeded();
|
||||||
void downloadFailed(QString reason);
|
void downloadFailed(QString reason);
|
||||||
void downloadProgressChanged(qint64 current, qint64 total);
|
void downloadProgressChanged(qint64 current, qint64 total);
|
||||||
|
void downloadAborted();
|
||||||
void extractFinished();
|
void extractFinished();
|
||||||
void extractAborted();
|
|
||||||
|
|
||||||
private: /* data */
|
private: /* data */
|
||||||
NetJob::Ptr m_filesNetJob;
|
NetJob::Ptr m_filesNetJob;
|
||||||
@ -61,12 +89,21 @@ private: /* data */
|
|||||||
QString m_archivePath;
|
QString m_archivePath;
|
||||||
bool m_downloadRequired = false;
|
bool m_downloadRequired = false;
|
||||||
std::unique_ptr<QuaZip> m_packZip;
|
std::unique_ptr<QuaZip> m_packZip;
|
||||||
QFuture<nonstd::optional<QStringList>> m_extractFuture;
|
QFuture<std::optional<QStringList>> m_extractFuture;
|
||||||
QFutureWatcher<nonstd::optional<QStringList>> m_extractFutureWatcher;
|
QFutureWatcher<std::optional<QStringList>> m_extractFutureWatcher;
|
||||||
|
QVector<Flame::File> m_blockedMods;
|
||||||
enum class ModpackType{
|
enum class ModpackType{
|
||||||
Unknown,
|
Unknown,
|
||||||
MultiMC,
|
MultiMC,
|
||||||
|
Technic,
|
||||||
Flame,
|
Flame,
|
||||||
Technic
|
Modrinth,
|
||||||
} m_modpackType = ModpackType::Unknown;
|
} m_modpackType = ModpackType::Unknown;
|
||||||
|
|
||||||
|
// Extra info we might need, that's available before, but can't be derived from
|
||||||
|
// the source URL / the resource it points to alone.
|
||||||
|
QMap<QString, QString> m_extra_info;
|
||||||
|
|
||||||
|
//FIXME: nuke
|
||||||
|
QWidget* m_parent;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,24 @@
|
|||||||
/* Copyright 2013-2021 MultiMC Contributors
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -13,30 +33,36 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QSet>
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QThread>
|
|
||||||
#include <QTextStream>
|
|
||||||
#include <QXmlStreamReader>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QUuid>
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QStack>
|
||||||
|
#include <QPair>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QUuid>
|
||||||
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
#include "InstanceList.h"
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
#include "InstanceTask.h"
|
|
||||||
#include "settings/INISettingsObject.h"
|
|
||||||
#include "NullInstance.h"
|
|
||||||
#include "minecraft/MinecraftInstance.h"
|
|
||||||
#include "FileSystem.h"
|
|
||||||
#include "ExponentialSeries.h"
|
#include "ExponentialSeries.h"
|
||||||
|
#include "FileSystem.h"
|
||||||
|
#include "InstanceList.h"
|
||||||
|
#include "InstanceTask.h"
|
||||||
|
#include "NullInstance.h"
|
||||||
#include "WatchLock.h"
|
#include "WatchLock.h"
|
||||||
|
#include "minecraft/MinecraftInstance.h"
|
||||||
|
#include "settings/INISettingsObject.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
const static int GROUP_FILE_FORMAT_VERSION = 1;
|
||||||
|
|
||||||
@ -45,8 +71,7 @@ InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir,
|
|||||||
{
|
{
|
||||||
resumeWatch();
|
resumeWatch();
|
||||||
// Create aand normalize path
|
// Create aand normalize path
|
||||||
if (!QDir::current().exists(instDir))
|
if (!QDir::current().exists(instDir)) {
|
||||||
{
|
|
||||||
QDir::current().mkpath(instDir);
|
QDir::current().mkpath(instDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,9 +84,7 @@ InstanceList::InstanceList(SettingsObjectPtr settings, const QString & instDir,
|
|||||||
m_watcher->addPath(m_instDir);
|
m_watcher->addPath(m_instDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceList::~InstanceList()
|
InstanceList::~InstanceList() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::DropActions InstanceList::supportedDragActions() const
|
Qt::DropActions InstanceList::supportedDragActions() const
|
||||||
{
|
{
|
||||||
@ -106,6 +129,15 @@ QMimeData * InstanceList::mimeData(const QModelIndexList& indexes) const
|
|||||||
return mimeData;
|
return mimeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList InstanceList::getLinkedInstancesById(const QString &id) const
|
||||||
|
{
|
||||||
|
QStringList linkedInstances;
|
||||||
|
for (auto inst : m_instances) {
|
||||||
|
if (inst->isLinkedToInstanceId(id))
|
||||||
|
linkedInstances.append(inst->id());
|
||||||
|
}
|
||||||
|
return linkedInstances;
|
||||||
|
}
|
||||||
|
|
||||||
int InstanceList::rowCount(const QModelIndex& parent) const
|
int InstanceList::rowCount(const QModelIndex& parent) const
|
||||||
{
|
{
|
||||||
@ -123,8 +155,7 @@ QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent)
|
|||||||
|
|
||||||
QVariant InstanceList::data(const QModelIndex& index, int role) const
|
QVariant InstanceList::data(const QModelIndex& index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid()) {
|
||||||
{
|
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
|
BaseInstance *pdata = static_cast<BaseInstance *>(index.internalPointer());
|
||||||
@ -132,7 +163,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
|
|||||||
{
|
{
|
||||||
case InstancePointerRole:
|
case InstancePointerRole:
|
||||||
{
|
{
|
||||||
QVariant v = qVariantFromValue((void *)pdata);
|
QVariant v = QVariant::fromValue((void *)pdata);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
case InstanceIDRole:
|
case InstanceIDRole:
|
||||||
@ -169,18 +200,15 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const
|
|||||||
|
|
||||||
bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int role)
|
bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||||
{
|
{
|
||||||
if (!index.isValid())
|
if (!index.isValid()) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(role != Qt::EditRole)
|
if (role != Qt::EditRole) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer());
|
BaseInstance* pdata = static_cast<BaseInstance*>(index.internalPointer());
|
||||||
auto newName = value.toString();
|
auto newName = value.toString();
|
||||||
if(pdata->name() == newName)
|
if (pdata->name() == newName) {
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
pdata->setName(newName);
|
pdata->setName(newName);
|
||||||
@ -190,8 +218,7 @@ bool InstanceList::setData(const QModelIndex& index, const QVariant& value, int
|
|||||||
Qt::ItemFlags InstanceList::flags(const QModelIndex& index) const
|
Qt::ItemFlags InstanceList::flags(const QModelIndex& index) const
|
||||||
{
|
{
|
||||||
Qt::ItemFlags f;
|
Qt::ItemFlags f;
|
||||||
if (index.isValid())
|
if (index.isValid()) {
|
||||||
{
|
|
||||||
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
|
||||||
}
|
}
|
||||||
return f;
|
return f;
|
||||||
@ -200,13 +227,11 @@ Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const
|
|||||||
GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
||||||
{
|
{
|
||||||
auto inst = getInstanceById(id);
|
auto inst = getInstanceById(id);
|
||||||
if(!inst)
|
if (!inst) {
|
||||||
{
|
|
||||||
return GroupId();
|
return GroupId();
|
||||||
}
|
}
|
||||||
auto iter = m_instanceGroupIndex.find(inst->id());
|
auto iter = m_instanceGroupIndex.find(inst->id());
|
||||||
if(iter != m_instanceGroupIndex.end())
|
if (iter != m_instanceGroupIndex.end()) {
|
||||||
{
|
|
||||||
return *iter;
|
return *iter;
|
||||||
}
|
}
|
||||||
return GroupId();
|
return GroupId();
|
||||||
@ -215,30 +240,24 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
|
|||||||
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
||||||
{
|
{
|
||||||
auto inst = getInstanceById(id);
|
auto inst = getInstanceById(id);
|
||||||
if(!inst)
|
if (!inst) {
|
||||||
{
|
|
||||||
qDebug() << "Attempt to set a null instance's group";
|
qDebug() << "Attempt to set a null instance's group";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
auto iter = m_instanceGroupIndex.find(inst->id());
|
auto iter = m_instanceGroupIndex.find(inst->id());
|
||||||
if(iter != m_instanceGroupIndex.end())
|
if (iter != m_instanceGroupIndex.end()) {
|
||||||
{
|
if (*iter != name) {
|
||||||
if(*iter != name)
|
|
||||||
{
|
|
||||||
*iter = name;
|
*iter = name;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
changed = true;
|
changed = true;
|
||||||
m_instanceGroupIndex[id] = name;
|
m_instanceGroupIndex[id] = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(changed)
|
if (changed) {
|
||||||
{
|
|
||||||
m_groupNameCache.insert(name);
|
m_groupNameCache.insert(name);
|
||||||
auto idx = getInstIndex(inst.get());
|
auto idx = getInstIndex(inst.get());
|
||||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||||
@ -248,31 +267,27 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
|
|||||||
|
|
||||||
QStringList InstanceList::getGroups()
|
QStringList InstanceList::getGroups()
|
||||||
{
|
{
|
||||||
return m_groupNameCache.toList();
|
return m_groupNameCache.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceList::deleteGroup(const QString& name)
|
void InstanceList::deleteGroup(const QString& name)
|
||||||
{
|
{
|
||||||
bool removed = false;
|
bool removed = false;
|
||||||
qDebug() << "Delete group" << name;
|
qDebug() << "Delete group" << name;
|
||||||
for(auto & instance: m_instances)
|
for (auto& instance : m_instances) {
|
||||||
{
|
|
||||||
const auto& instID = instance->id();
|
const auto& instID = instance->id();
|
||||||
auto instGroupName = getInstanceGroup(instID);
|
auto instGroupName = getInstanceGroup(instID);
|
||||||
if(instGroupName == name)
|
if (instGroupName == name) {
|
||||||
{
|
|
||||||
m_instanceGroupIndex.remove(instID);
|
m_instanceGroupIndex.remove(instID);
|
||||||
qDebug() << "Remove" << instID << "from group" << name;
|
qDebug() << "Remove" << instID << "from group" << name;
|
||||||
removed = true;
|
removed = true;
|
||||||
auto idx = getInstIndex(instance.get());
|
auto idx = getInstIndex(instance.get());
|
||||||
if(idx > 0)
|
if (idx > 0) {
|
||||||
{
|
|
||||||
emit dataChanged(index(idx), index(idx), { GroupRole });
|
emit dataChanged(index(idx), index(idx), { GroupRole });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(removed)
|
if (removed) {
|
||||||
{
|
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,23 +297,75 @@ bool InstanceList::isGroupCollapsed(const QString& group)
|
|||||||
return m_collapsedGroups.contains(group);
|
return m_collapsedGroups.contains(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InstanceList::trashInstance(const InstanceId& id)
|
||||||
|
{
|
||||||
|
auto inst = getInstanceById(id);
|
||||||
|
if (!inst) {
|
||||||
|
qDebug() << "Cannot trash instance" << id << ". No such instance is present (deleted externally?).";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cachedGroupId = m_instanceGroupIndex[id];
|
||||||
|
|
||||||
|
qDebug() << "Will trash instance" << id;
|
||||||
|
QString trashedLoc;
|
||||||
|
|
||||||
|
if (m_instanceGroupIndex.remove(id)) {
|
||||||
|
saveGroupList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FS::trash(inst->instanceRoot(), &trashedLoc)) {
|
||||||
|
qDebug() << "Trash of instance" << id << "has not been completely successfully...";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Instance" << id << "has been trashed by the launcher.";
|
||||||
|
m_trashHistory.push({id, inst->instanceRoot(), trashedLoc, cachedGroupId});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstanceList::trashedSomething() {
|
||||||
|
return !m_trashHistory.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceList::undoTrashInstance() {
|
||||||
|
if (m_trashHistory.empty()) {
|
||||||
|
qWarning() << "Nothing to recover from trash.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto top = m_trashHistory.pop();
|
||||||
|
|
||||||
|
while (QDir(top.polyPath).exists()) {
|
||||||
|
top.id += "1";
|
||||||
|
top.polyPath += "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "Moving" << top.trashPath << "back to" << top.polyPath;
|
||||||
|
QFile(top.trashPath).rename(top.polyPath);
|
||||||
|
|
||||||
|
m_instanceGroupIndex[top.id] = top.groupName;
|
||||||
|
m_groupNameCache.insert(top.groupName);
|
||||||
|
|
||||||
|
saveGroupList();
|
||||||
|
emit instancesChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceList::deleteInstance(const InstanceId& id)
|
void InstanceList::deleteInstance(const InstanceId& id)
|
||||||
{
|
{
|
||||||
auto inst = getInstanceById(id);
|
auto inst = getInstanceById(id);
|
||||||
if(!inst)
|
if (!inst) {
|
||||||
{
|
|
||||||
qDebug() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?).";
|
qDebug() << "Cannot delete instance" << id << ". No such instance is present (deleted externally?).";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_instanceGroupIndex.remove(id))
|
if (m_instanceGroupIndex.remove(id)) {
|
||||||
{
|
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "Will delete instance" << id;
|
qDebug() << "Will delete instance" << id;
|
||||||
if(!FS::deletePath(inst->instanceRoot()))
|
if (!FS::deletePath(inst->instanceRoot())) {
|
||||||
{
|
|
||||||
qWarning() << "Deletion of instance" << id << "has not been completely successful ...";
|
qWarning() << "Deletion of instance" << id << "has not been completely successful ...";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -310,11 +377,9 @@ static QMap<InstanceId, InstanceLocator> getIdMapping(const QList<InstancePtr> &
|
|||||||
{
|
{
|
||||||
QMap<InstanceId, InstanceLocator> out;
|
QMap<InstanceId, InstanceLocator> out;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(auto & item: list)
|
for (auto& item : list) {
|
||||||
{
|
|
||||||
auto id = item->id();
|
auto id = item->id();
|
||||||
if(out.contains(id))
|
if (out.contains(id)) {
|
||||||
{
|
|
||||||
qWarning() << "Duplicate ID" << id << "in instance list";
|
qWarning() << "Duplicate ID" << id << "in instance list";
|
||||||
}
|
}
|
||||||
out[id] = std::make_pair(item, i);
|
out[id] = std::make_pair(item, i);
|
||||||
@ -328,19 +393,16 @@ QList< InstanceId > InstanceList::discoverInstances()
|
|||||||
qDebug() << "Discovering instances in" << m_instDir;
|
qDebug() << "Discovering instances in" << m_instDir;
|
||||||
QList<InstanceId> out;
|
QList<InstanceId> out;
|
||||||
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
|
QDirIterator iter(m_instDir, QDir::Dirs | QDir::NoDot | QDir::NoDotDot | QDir::Readable | QDir::Hidden, QDirIterator::FollowSymlinks);
|
||||||
while (iter.hasNext())
|
while (iter.hasNext()) {
|
||||||
{
|
|
||||||
QString subDir = iter.next();
|
QString subDir = iter.next();
|
||||||
QFileInfo dirInfo(subDir);
|
QFileInfo dirInfo(subDir);
|
||||||
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
|
if (!QFileInfo(FS::PathCombine(subDir, "instance.cfg")).exists())
|
||||||
continue;
|
continue;
|
||||||
// if it is a symlink, ignore it if it goes to the instance folder
|
// if it is a symlink, ignore it if it goes to the instance folder
|
||||||
if(dirInfo.isSymLink())
|
if (dirInfo.isSymLink()) {
|
||||||
{
|
|
||||||
QFileInfo targetInfo(dirInfo.symLinkTarget());
|
QFileInfo targetInfo(dirInfo.symLinkTarget());
|
||||||
QFileInfo instDirInfo(m_instDir);
|
QFileInfo instDirInfo(m_instDir);
|
||||||
if(targetInfo.canonicalPath() == instDirInfo.canonicalFilePath())
|
if (targetInfo.canonicalPath() == instDirInfo.canonicalFilePath()) {
|
||||||
{
|
|
||||||
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
|
qDebug() << "Ignoring symlink" << subDir << "that leads into the instances folder";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -349,7 +411,11 @@ QList< InstanceId > InstanceList::discoverInstances()
|
|||||||
out.append(id);
|
out.append(id);
|
||||||
qDebug() << "Found instance ID" << id;
|
qDebug() << "Found instance ID" << id;
|
||||||
}
|
}
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
instanceSet = QSet<QString>(out.begin(), out.end());
|
||||||
|
#else
|
||||||
instanceSet = out.toSet();
|
instanceSet = out.toSet();
|
||||||
|
#endif
|
||||||
m_instancesProbed = true;
|
m_instancesProbed = true;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@ -360,74 +426,56 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
|
|
||||||
QList<InstancePtr> newList;
|
QList<InstancePtr> newList;
|
||||||
|
|
||||||
for(auto & id: discoverInstances())
|
for (auto& id : discoverInstances()) {
|
||||||
{
|
if (existingIds.contains(id)) {
|
||||||
if(existingIds.contains(id))
|
|
||||||
{
|
|
||||||
auto instPair = existingIds[id];
|
auto instPair = existingIds[id];
|
||||||
existingIds.remove(id);
|
existingIds.remove(id);
|
||||||
qDebug() << "Should keep and soft-reload" << id;
|
qDebug() << "Should keep and soft-reload" << id;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
InstancePtr instPtr = loadInstance(id);
|
InstancePtr instPtr = loadInstance(id);
|
||||||
if(instPtr)
|
if (instPtr) {
|
||||||
{
|
|
||||||
newList.append(instPtr);
|
newList.append(instPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
|
// TODO: looks like a general algorithm with a few specifics inserted. Do something about it.
|
||||||
if(!existingIds.isEmpty())
|
if (!existingIds.isEmpty()) {
|
||||||
{
|
|
||||||
// get the list of removed instances and sort it by their original index, from last to first
|
// get the list of removed instances and sort it by their original index, from last to first
|
||||||
auto deadList = existingIds.values();
|
auto deadList = existingIds.values();
|
||||||
auto orderSortPredicate = [](const InstanceLocator & a, const InstanceLocator & b) -> bool
|
auto orderSortPredicate = [](const InstanceLocator& a, const InstanceLocator& b) -> bool { return a.second > b.second; };
|
||||||
{
|
|
||||||
return a.second > b.second;
|
|
||||||
};
|
|
||||||
std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
|
std::sort(deadList.begin(), deadList.end(), orderSortPredicate);
|
||||||
// remove the contiguous ranges of rows
|
// remove the contiguous ranges of rows
|
||||||
int front_bookmark = -1;
|
int front_bookmark = -1;
|
||||||
int back_bookmark = -1;
|
int back_bookmark = -1;
|
||||||
int currentItem = -1;
|
int currentItem = -1;
|
||||||
auto removeNow = [&]()
|
auto removeNow = [&]() {
|
||||||
{
|
|
||||||
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
|
beginRemoveRows(QModelIndex(), front_bookmark, back_bookmark);
|
||||||
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
|
m_instances.erase(m_instances.begin() + front_bookmark, m_instances.begin() + back_bookmark + 1);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
front_bookmark = -1;
|
front_bookmark = -1;
|
||||||
back_bookmark = currentItem;
|
back_bookmark = currentItem;
|
||||||
};
|
};
|
||||||
for(auto & removedItem: deadList)
|
for (auto& removedItem : deadList) {
|
||||||
{
|
|
||||||
auto instPtr = removedItem.first;
|
auto instPtr = removedItem.first;
|
||||||
instPtr->invalidate();
|
instPtr->invalidate();
|
||||||
currentItem = removedItem.second;
|
currentItem = removedItem.second;
|
||||||
if(back_bookmark == -1)
|
if (back_bookmark == -1) {
|
||||||
{
|
|
||||||
// no bookmark yet
|
// no bookmark yet
|
||||||
back_bookmark = currentItem;
|
back_bookmark = currentItem;
|
||||||
}
|
} else if (currentItem == front_bookmark - 1) {
|
||||||
else if(currentItem == front_bookmark - 1)
|
|
||||||
{
|
|
||||||
// part of contiguous sequence, continue
|
// part of contiguous sequence, continue
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// seam between previous and current item
|
// seam between previous and current item
|
||||||
removeNow();
|
removeNow();
|
||||||
}
|
}
|
||||||
front_bookmark = currentItem;
|
front_bookmark = currentItem;
|
||||||
}
|
}
|
||||||
if(back_bookmark != -1)
|
if (back_bookmark != -1) {
|
||||||
{
|
|
||||||
removeNow();
|
removeNow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(newList.size())
|
if (newList.size()) {
|
||||||
{
|
|
||||||
add(newList);
|
add(newList);
|
||||||
}
|
}
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
@ -438,16 +486,14 @@ InstanceList::InstListError InstanceList::loadList()
|
|||||||
void InstanceList::updateTotalPlayTime()
|
void InstanceList::updateTotalPlayTime()
|
||||||
{
|
{
|
||||||
totalPlayTime = 0;
|
totalPlayTime = 0;
|
||||||
for(auto const& itr : m_instances)
|
for (auto const& itr : m_instances) {
|
||||||
{
|
|
||||||
totalPlayTime += itr.get()->totalTimePlayed();
|
totalPlayTime += itr.get()->totalTimePlayed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstanceList::saveNow()
|
void InstanceList::saveNow()
|
||||||
{
|
{
|
||||||
for(auto & item: m_instances)
|
for (auto& item : m_instances) {
|
||||||
{
|
|
||||||
item->saveNow();
|
item->saveNow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,8 +502,7 @@ void InstanceList::add(const QList<InstancePtr> &t)
|
|||||||
{
|
{
|
||||||
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
|
beginInsertRows(QModelIndex(), m_instances.count(), m_instances.count() + t.size() - 1);
|
||||||
m_instances.append(t);
|
m_instances.append(t);
|
||||||
for(auto & ptr : t)
|
for (auto& ptr : t) {
|
||||||
{
|
|
||||||
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
|
connect(ptr.get(), &BaseInstance::propertiesChanged, this, &InstanceList::propertiesChanged);
|
||||||
}
|
}
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
@ -465,14 +510,12 @@ void InstanceList::add(const QList<InstancePtr> &t)
|
|||||||
|
|
||||||
void InstanceList::resumeWatch()
|
void InstanceList::resumeWatch()
|
||||||
{
|
{
|
||||||
if(m_watchLevel > 0)
|
if (m_watchLevel > 0) {
|
||||||
{
|
|
||||||
qWarning() << "Bad suspend level resume in instance list";
|
qWarning() << "Bad suspend level resume in instance list";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_watchLevel++;
|
m_watchLevel++;
|
||||||
if(m_watchLevel > 0 && m_dirty)
|
if (m_watchLevel > 0 && m_dirty) {
|
||||||
{
|
|
||||||
loadList();
|
loadList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,8 +528,7 @@ void InstanceList::suspendWatch()
|
|||||||
void InstanceList::providerUpdated()
|
void InstanceList::providerUpdated()
|
||||||
{
|
{
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
if(m_watchLevel == 1)
|
if (m_watchLevel == 1) {
|
||||||
{
|
|
||||||
loadList();
|
loadList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -495,16 +537,27 @@ InstancePtr InstanceList::getInstanceById(QString instId) const
|
|||||||
{
|
{
|
||||||
if (instId.isEmpty())
|
if (instId.isEmpty())
|
||||||
return InstancePtr();
|
return InstancePtr();
|
||||||
for(auto & inst: m_instances)
|
for (auto& inst : m_instances) {
|
||||||
{
|
if (inst->id() == instId) {
|
||||||
if (inst->id() == instId)
|
|
||||||
{
|
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return InstancePtr();
|
return InstancePtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InstancePtr InstanceList::getInstanceByManagedName(const QString& managed_name) const
|
||||||
|
{
|
||||||
|
if (managed_name.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
for (auto instance : m_instances) {
|
||||||
|
if (instance->getManagedPackName() == managed_name)
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
|
QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
|
||||||
{
|
{
|
||||||
return index(getInstIndex(getInstanceById(id).get()));
|
return index(getInstIndex(getInstanceById(id).get()));
|
||||||
@ -513,10 +566,8 @@ QModelIndex InstanceList::getInstanceIndexById(const QString &id) const
|
|||||||
int InstanceList::getInstIndex(BaseInstance* inst) const
|
int InstanceList::getInstIndex(BaseInstance* inst) const
|
||||||
{
|
{
|
||||||
int count = m_instances.count();
|
int count = m_instances.count();
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++) {
|
||||||
{
|
if (inst == m_instances[i].get()) {
|
||||||
if (inst == m_instances[i].get())
|
|
||||||
{
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -526,8 +577,7 @@ int InstanceList::getInstIndex(BaseInstance *inst) const
|
|||||||
void InstanceList::propertiesChanged(BaseInstance* inst)
|
void InstanceList::propertiesChanged(BaseInstance* inst)
|
||||||
{
|
{
|
||||||
int i = getInstIndex(inst);
|
int i = getInstIndex(inst);
|
||||||
if (i != -1)
|
if (i != -1) {
|
||||||
{
|
|
||||||
emit dataChanged(index(i), index(i));
|
emit dataChanged(index(i), index(i));
|
||||||
updateTotalPlayTime();
|
updateTotalPlayTime();
|
||||||
}
|
}
|
||||||
@ -535,16 +585,27 @@ void InstanceList::propertiesChanged(BaseInstance *inst)
|
|||||||
|
|
||||||
InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
||||||
{
|
{
|
||||||
if(!m_groupsLoaded)
|
if (!m_groupsLoaded) {
|
||||||
{
|
|
||||||
loadGroupList();
|
loadGroupList();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto instanceRoot = FS::PathCombine(m_instDir, id);
|
auto instanceRoot = FS::PathCombine(m_instDir, id);
|
||||||
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
|
auto instanceSettings = std::make_shared<INISettingsObject>(FS::PathCombine(instanceRoot, "instance.cfg"));
|
||||||
InstancePtr inst;
|
InstancePtr inst;
|
||||||
// TODO: Handle incompatible instances
|
|
||||||
|
instanceSettings->registerSetting("InstanceType", "");
|
||||||
|
|
||||||
|
QString inst_type = instanceSettings->get("InstanceType").toString();
|
||||||
|
|
||||||
|
// NOTE: Some PolyMC versions didn't save the InstanceType properly. We will just bank on the probability that this is probably a OneSix instance
|
||||||
|
if (inst_type == "OneSix" || inst_type.isEmpty())
|
||||||
|
{
|
||||||
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
inst.reset(new MinecraftInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inst.reset(new NullInstance(m_globalSettings, instanceSettings, instanceRoot));
|
||||||
|
}
|
||||||
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
|
qDebug() << "Loaded instance " << inst->name() << " from " << inst->instanceRoot();
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
@ -552,34 +613,28 @@ InstancePtr InstanceList::loadInstance(const InstanceId& id)
|
|||||||
void InstanceList::saveGroupList()
|
void InstanceList::saveGroupList()
|
||||||
{
|
{
|
||||||
qDebug() << "Will save group list now.";
|
qDebug() << "Will save group list now.";
|
||||||
if(!m_instancesProbed)
|
if (!m_instancesProbed) {
|
||||||
{
|
|
||||||
qDebug() << "Group saving prevented because we don't know the full list of instances yet.";
|
qDebug() << "Group saving prevented because we don't know the full list of instances yet.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WatchLock foo(m_watcher, m_instDir);
|
WatchLock foo(m_watcher, m_instDir);
|
||||||
QString groupFileName = m_instDir + "/instgroups.json";
|
QString groupFileName = m_instDir + "/instgroups.json";
|
||||||
QMap<QString, QSet<QString>> reverseGroupMap;
|
QMap<QString, QSet<QString>> reverseGroupMap;
|
||||||
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++)
|
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++) {
|
||||||
{
|
|
||||||
QString id = iter.key();
|
QString id = iter.key();
|
||||||
QString group = iter.value();
|
QString group = iter.value();
|
||||||
if (group.isEmpty())
|
if (group.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
if(!instanceSet.contains(id))
|
if (!instanceSet.contains(id)) {
|
||||||
{
|
|
||||||
qDebug() << "Skipping saving missing instance" << id << "to groups list.";
|
qDebug() << "Skipping saving missing instance" << id << "to groups list.";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reverseGroupMap.count(group))
|
if (!reverseGroupMap.count(group)) {
|
||||||
{
|
|
||||||
QSet<QString> set;
|
QSet<QString> set;
|
||||||
set.insert(id);
|
set.insert(id);
|
||||||
reverseGroupMap[group] = set;
|
reverseGroupMap[group] = set;
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
QSet<QString>& set = reverseGroupMap[group];
|
QSet<QString>& set = reverseGroupMap[group];
|
||||||
set.insert(id);
|
set.insert(id);
|
||||||
}
|
}
|
||||||
@ -587,15 +642,13 @@ void InstanceList::saveGroupList()
|
|||||||
QJsonObject toplevel;
|
QJsonObject toplevel;
|
||||||
toplevel.insert("formatVersion", QJsonValue(QString("1")));
|
toplevel.insert("formatVersion", QJsonValue(QString("1")));
|
||||||
QJsonObject groupsArr;
|
QJsonObject groupsArr;
|
||||||
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++)
|
for (auto iter = reverseGroupMap.begin(); iter != reverseGroupMap.end(); iter++) {
|
||||||
{
|
|
||||||
auto list = iter.value();
|
auto list = iter.value();
|
||||||
auto name = iter.key();
|
auto name = iter.key();
|
||||||
QJsonObject groupObj;
|
QJsonObject groupObj;
|
||||||
QJsonArray instanceArr;
|
QJsonArray instanceArr;
|
||||||
groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name)));
|
groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name)));
|
||||||
for (auto item : list)
|
for (auto item : list) {
|
||||||
{
|
|
||||||
instanceArr.append(QJsonValue(item));
|
instanceArr.append(QJsonValue(item));
|
||||||
}
|
}
|
||||||
groupObj.insert("instances", instanceArr);
|
groupObj.insert("instances", instanceArr);
|
||||||
@ -603,13 +656,10 @@ void InstanceList::saveGroupList()
|
|||||||
}
|
}
|
||||||
toplevel.insert("groups", groupsArr);
|
toplevel.insert("groups", groupsArr);
|
||||||
QJsonDocument doc(toplevel);
|
QJsonDocument doc(toplevel);
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
FS::write(groupFileName, doc.toJson());
|
FS::write(groupFileName, doc.toJson());
|
||||||
qDebug() << "Group list saved.";
|
qDebug() << "Group list saved.";
|
||||||
}
|
} catch (const FS::FileSystemException& e) {
|
||||||
catch (const FS::FileSystemException &e)
|
|
||||||
{
|
|
||||||
qCritical() << "Failed to write instance group file :" << e.cause();
|
qCritical() << "Failed to write instance group file :" << e.cause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -625,12 +675,9 @@ void InstanceList::loadGroupList()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QByteArray jsonData;
|
QByteArray jsonData;
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
jsonData = FS::read(groupFileName);
|
jsonData = FS::read(groupFileName);
|
||||||
}
|
} catch (const FS::FileSystemException& e) {
|
||||||
catch (const FS::FileSystemException &e)
|
|
||||||
{
|
|
||||||
qCritical() << "Failed to read instance group file :" << e.cause();
|
qCritical() << "Failed to read instance group file :" << e.cause();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -639,8 +686,7 @@ void InstanceList::loadGroupList()
|
|||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &error);
|
||||||
|
|
||||||
// if the json was bad, fail
|
// if the json was bad, fail
|
||||||
if (error.error != QJsonParseError::NoError)
|
if (error.error != QJsonParseError::NoError) {
|
||||||
{
|
|
||||||
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
|
qCritical() << QString("Failed to parse instance group file: %1 at offset %2")
|
||||||
.arg(error.errorString(), QString::number(error.offset))
|
.arg(error.errorString(), QString::number(error.offset))
|
||||||
.toUtf8();
|
.toUtf8();
|
||||||
@ -648,8 +694,7 @@ void InstanceList::loadGroupList()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if the root of the json wasn't an object, fail
|
// if the root of the json wasn't an object, fail
|
||||||
if (!jsonDoc.isObject())
|
if (!jsonDoc.isObject()) {
|
||||||
{
|
|
||||||
qWarning() << "Invalid group file. Root entry should be an object.";
|
qWarning() << "Invalid group file. Root entry should be an object.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -661,8 +706,7 @@ void InstanceList::loadGroupList()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Get the groups. if it's not an object, fail
|
// Get the groups. if it's not an object, fail
|
||||||
if (!rootObj.value("groups").isObject())
|
if (!rootObj.value("groups").isObject()) {
|
||||||
{
|
|
||||||
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
|
qWarning() << "Invalid group list JSON: 'groups' should be an object.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -672,21 +716,20 @@ void InstanceList::loadGroupList()
|
|||||||
|
|
||||||
// Iterate through all the groups.
|
// Iterate through all the groups.
|
||||||
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
||||||
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++)
|
for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) {
|
||||||
{
|
|
||||||
QString groupName = iter.key();
|
QString groupName = iter.key();
|
||||||
|
|
||||||
// If not an object, complain and skip to the next one.
|
// If not an object, complain and skip to the next one.
|
||||||
if (!iter.value().isObject())
|
if (!iter.value().isObject()) {
|
||||||
{
|
|
||||||
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8();
|
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject groupObj = iter.value().toObject();
|
QJsonObject groupObj = iter.value().toObject();
|
||||||
if (!groupObj.value("instances").isArray())
|
if (!groupObj.value("instances").isArray()) {
|
||||||
{
|
qWarning() << QString("Group '%1' in the group list is invalid. It should contain an array called 'instances'.")
|
||||||
qWarning() << QString("Group '%1' in the group list is invalid. It should contain an array called 'instances'.").arg(groupName).toUtf8();
|
.arg(groupName)
|
||||||
|
.toUtf8();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -701,8 +744,7 @@ void InstanceList::loadGroupList()
|
|||||||
// Iterate through the list of instances in the group.
|
// Iterate through the list of instances in the group.
|
||||||
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
QJsonArray instancesArray = groupObj.value("instances").toArray();
|
||||||
|
|
||||||
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++)
|
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++) {
|
||||||
{
|
|
||||||
m_instanceGroupIndex[(*iter2).toString()] = groupName;
|
m_instanceGroupIndex[(*iter2).toString()] = groupName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -720,10 +762,8 @@ void InstanceList::instanceDirContentsChanged(const QString& path)
|
|||||||
void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value)
|
void InstanceList::on_InstFolderChanged(const Setting& setting, QVariant value)
|
||||||
{
|
{
|
||||||
QString newInstDir = QDir(value.toString()).canonicalPath();
|
QString newInstDir = QDir(value.toString()).canonicalPath();
|
||||||
if(newInstDir != m_instDir)
|
if (newInstDir != m_instDir) {
|
||||||
{
|
if (m_groupsLoaded) {
|
||||||
if(m_groupsLoaded)
|
|
||||||
{
|
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
}
|
}
|
||||||
m_instDir = newInstDir;
|
m_instDir = newInstDir;
|
||||||
@ -743,80 +783,62 @@ void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
|
|||||||
saveGroupList();
|
saveGroupList();
|
||||||
}
|
}
|
||||||
|
|
||||||
class InstanceStaging : public Task
|
class InstanceStaging : public Task {
|
||||||
{
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
const unsigned minBackoff = 1;
|
const unsigned minBackoff = 1;
|
||||||
const unsigned maxBackoff = 16;
|
const unsigned maxBackoff = 16;
|
||||||
public:
|
public:
|
||||||
InstanceStaging (
|
InstanceStaging(InstanceList* parent, InstanceTask* child, QString stagingPath, InstanceName const& instanceName, QString groupName)
|
||||||
InstanceList * parent,
|
: m_parent(parent), backoff(minBackoff, maxBackoff), m_stagingPath(std::move(stagingPath)), m_instance_name(std::move(instanceName)), m_groupName(std::move(groupName))
|
||||||
Task * child,
|
|
||||||
const QString & stagingPath,
|
|
||||||
const QString& instanceName,
|
|
||||||
const QString& groupName )
|
|
||||||
: backoff(minBackoff, maxBackoff)
|
|
||||||
{
|
{
|
||||||
m_parent = parent;
|
|
||||||
m_child.reset(child);
|
m_child.reset(child);
|
||||||
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded);
|
connect(child, &Task::succeeded, this, &InstanceStaging::childSucceded);
|
||||||
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
connect(child, &Task::failed, this, &InstanceStaging::childFailed);
|
||||||
|
connect(child, &Task::aborted, this, &InstanceStaging::childAborted);
|
||||||
|
connect(child, &Task::abortStatusChanged, this, &InstanceStaging::setAbortable);
|
||||||
connect(child, &Task::status, this, &InstanceStaging::setStatus);
|
connect(child, &Task::status, this, &InstanceStaging::setStatus);
|
||||||
|
connect(child, &Task::details, this, &InstanceStaging::setDetails);
|
||||||
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
|
connect(child, &Task::progress, this, &InstanceStaging::setProgress);
|
||||||
m_instanceName = instanceName;
|
connect(child, &Task::stepProgress, this, &InstanceStaging::propogateStepProgress);
|
||||||
m_groupName = groupName;
|
|
||||||
m_stagingPath = stagingPath;
|
|
||||||
m_backoffTimer.setSingleShot(true);
|
|
||||||
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
|
connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceded);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~InstanceStaging(){};
|
virtual ~InstanceStaging(){};
|
||||||
|
|
||||||
|
|
||||||
// FIXME/TODO: add ability to abort during instance commit retries
|
// FIXME/TODO: add ability to abort during instance commit retries
|
||||||
bool abort() override
|
bool abort() override
|
||||||
{
|
{
|
||||||
if(m_child && m_child->canAbort())
|
if (!canAbort())
|
||||||
{
|
|
||||||
return m_child->abort();
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
m_child->abort();
|
||||||
|
|
||||||
|
return Task::abort();
|
||||||
}
|
}
|
||||||
bool canAbort() const override
|
bool canAbort() const override
|
||||||
{
|
{
|
||||||
if(m_child && m_child->canAbort())
|
return (m_child && m_child->canAbort());
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void executeTask() override
|
virtual void executeTask() override { m_child->start(); }
|
||||||
{
|
QStringList warnings() const override { return m_child->warnings(); }
|
||||||
m_child->start();
|
|
||||||
}
|
|
||||||
QStringList warnings() const override
|
|
||||||
{
|
|
||||||
return m_child->warnings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void childSucceded()
|
void childSucceded()
|
||||||
{
|
{
|
||||||
unsigned sleepTime = backoff();
|
unsigned sleepTime = backoff();
|
||||||
if(m_parent->commitStagedInstance(m_stagingPath, m_instanceName, m_groupName))
|
if (m_parent->commitStagedInstance(m_stagingPath, m_instance_name, m_groupName, *m_child.get()))
|
||||||
{
|
{
|
||||||
emitSucceeded();
|
emitSucceeded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// we actually failed, retry?
|
// we actually failed, retry?
|
||||||
if(sleepTime == maxBackoff)
|
if (sleepTime == maxBackoff) {
|
||||||
{
|
|
||||||
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
|
emitFailed(tr("Failed to commit instance, even after multiple retries. It is being blocked by something."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qDebug() << "Failed to commit instance" << m_instanceName << "Initiating backoff:" << sleepTime;
|
qDebug() << "Failed to commit instance" << m_instance_name.name() << "Initiating backoff:" << sleepTime;
|
||||||
m_backoffTimer.start(sleepTime * 500);
|
m_backoffTimer.start(sleepTime * 500);
|
||||||
}
|
}
|
||||||
void childFailed(const QString& reason)
|
void childFailed(const QString& reason)
|
||||||
@ -825,7 +847,13 @@ private slots:
|
|||||||
emitFailed(reason);
|
emitFailed(reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void childAborted()
|
||||||
|
{
|
||||||
|
emitAborted();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
InstanceList * m_parent;
|
||||||
/*
|
/*
|
||||||
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
|
* WHY: the whole reason why this uses an exponential backoff retry scheme is antivirus on Windows.
|
||||||
* Basically, it starts messing things up while the launcher is extracting/creating instances
|
* Basically, it starts messing things up while the launcher is extracting/creating instances
|
||||||
@ -833,9 +861,8 @@ private:
|
|||||||
*/
|
*/
|
||||||
ExponentialSeries backoff;
|
ExponentialSeries backoff;
|
||||||
QString m_stagingPath;
|
QString m_stagingPath;
|
||||||
InstanceList * m_parent;
|
unique_qobject_ptr<InstanceTask> m_child;
|
||||||
unique_qobject_ptr<Task> m_child;
|
InstanceName m_instance_name;
|
||||||
QString m_instanceName;
|
|
||||||
QString m_groupName;
|
QString m_groupName;
|
||||||
QTimer m_backoffTimer;
|
QTimer m_backoffTimer;
|
||||||
};
|
};
|
||||||
@ -845,40 +872,67 @@ Task * InstanceList::wrapInstanceTask(InstanceTask * task)
|
|||||||
auto stagingPath = getStagedInstancePath();
|
auto stagingPath = getStagedInstancePath();
|
||||||
task->setStagingPath(stagingPath);
|
task->setStagingPath(stagingPath);
|
||||||
task->setParentSettings(m_globalSettings);
|
task->setParentSettings(m_globalSettings);
|
||||||
return new InstanceStaging(this, task, stagingPath, task->name(), task->group());
|
return new InstanceStaging(this, task, stagingPath, *task, task->group());
|
||||||
}
|
}
|
||||||
|
|
||||||
QString InstanceList::getStagedInstancePath()
|
QString InstanceList::getStagedInstancePath()
|
||||||
{
|
{
|
||||||
QString key = QUuid::createUuid().toString();
|
QString key = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||||
QString relPath = FS::PathCombine("_LAUNCHER_TEMP/" , key);
|
QString tempDir = ".LAUNCHER_TEMP/";
|
||||||
|
QString relPath = FS::PathCombine(tempDir, key);
|
||||||
QDir rootPath(m_instDir);
|
QDir rootPath(m_instDir);
|
||||||
auto path = FS::PathCombine(m_instDir, relPath);
|
auto path = FS::PathCombine(m_instDir, relPath);
|
||||||
if(!rootPath.mkpath(relPath))
|
if (!rootPath.mkpath(relPath)) {
|
||||||
{
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
auto tempPath = FS::PathCombine(m_instDir, tempDir);
|
||||||
|
SetFileAttributesA(tempPath.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED);
|
||||||
|
#endif
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InstanceList::commitStagedInstance(const QString& path, const QString& instanceName, const QString& groupName)
|
bool InstanceList::commitStagedInstance(const QString& path, InstanceName const& instanceName, const QString& groupName, InstanceTask const& commiting)
|
||||||
{
|
{
|
||||||
QDir dir;
|
QDir dir;
|
||||||
QString instID = FS::DirNameFromString(instanceName, m_instDir);
|
QString instID;
|
||||||
|
InstancePtr inst;
|
||||||
|
|
||||||
|
auto should_override = commiting.shouldOverride();
|
||||||
|
|
||||||
|
if (should_override) {
|
||||||
|
instID = commiting.originalInstanceID();
|
||||||
|
} else {
|
||||||
|
instID = FS::DirNameFromString(instanceName.modifiedName(), m_instDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_ASSERT(!instID.isEmpty());
|
||||||
|
|
||||||
{
|
{
|
||||||
WatchLock lock(m_watcher, m_instDir);
|
WatchLock lock(m_watcher, m_instDir);
|
||||||
QString destination = FS::PathCombine(m_instDir, instID);
|
QString destination = FS::PathCombine(m_instDir, instID);
|
||||||
if(!dir.rename(path, destination))
|
|
||||||
{
|
if (should_override) {
|
||||||
|
if (!FS::overrideFolder(destination, path)) {
|
||||||
|
qWarning() << "Failed to override" << path << "to" << destination;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!dir.rename(path, destination)) {
|
||||||
qWarning() << "Failed to move" << path << "to" << destination;
|
qWarning() << "Failed to move" << path << "to" << destination;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_instanceGroupIndex[instID] = groupName;
|
m_instanceGroupIndex[instID] = groupName;
|
||||||
instanceSet.insert(instID);
|
|
||||||
m_groupNameCache.insert(groupName);
|
m_groupNameCache.insert(groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceSet.insert(instID);
|
||||||
|
|
||||||
emit instancesChanged();
|
emit instancesChanged();
|
||||||
emit instanceSelectRequest(instID);
|
emit instanceSelectRequest(instID);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveGroupList();
|
saveGroupList();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -888,7 +942,8 @@ bool InstanceList::destroyStagingPath(const QString& keyPath)
|
|||||||
return FS::deletePath(keyPath);
|
return FS::deletePath(keyPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
int InstanceList::getTotalPlayTime() {
|
int InstanceList::getTotalPlayTime()
|
||||||
|
{
|
||||||
updateTotalPlayTime();
|
updateTotalPlayTime();
|
||||||
return totalPlayTime;
|
return totalPlayTime;
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,15 @@
|
|||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QStack>
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
#include "BaseInstance.h"
|
#include "BaseInstance.h"
|
||||||
|
|
||||||
#include "QObjectPtr.h"
|
|
||||||
|
|
||||||
class QFileSystemWatcher;
|
class QFileSystemWatcher;
|
||||||
class InstanceTask;
|
class InstanceTask;
|
||||||
|
struct InstanceName;
|
||||||
|
|
||||||
using InstanceId = QString;
|
using InstanceId = QString;
|
||||||
using GroupId = QString;
|
using GroupId = QString;
|
||||||
using InstanceLocator = std::pair<InstancePtr, int>;
|
using InstanceLocator = std::pair<InstancePtr, int>;
|
||||||
@ -46,6 +48,12 @@ enum class GroupsState
|
|||||||
Dirty
|
Dirty
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TrashHistoryItem {
|
||||||
|
QString id;
|
||||||
|
QString polyPath;
|
||||||
|
QString trashPath;
|
||||||
|
QString groupName;
|
||||||
|
};
|
||||||
|
|
||||||
class InstanceList : public QAbstractListModel
|
class InstanceList : public QAbstractListModel
|
||||||
{
|
{
|
||||||
@ -93,7 +101,10 @@ public:
|
|||||||
InstListError loadList();
|
InstListError loadList();
|
||||||
void saveNow();
|
void saveNow();
|
||||||
|
|
||||||
|
/* O(n) */
|
||||||
InstancePtr getInstanceById(QString id) const;
|
InstancePtr getInstanceById(QString id) const;
|
||||||
|
/* O(n) */
|
||||||
|
InstancePtr getInstanceByManagedName(const QString& managed_name) const;
|
||||||
QModelIndex getInstanceIndexById(const QString &id) const;
|
QModelIndex getInstanceIndexById(const QString &id) const;
|
||||||
QStringList getGroups();
|
QStringList getGroups();
|
||||||
bool isGroupCollapsed(const QString &groupName);
|
bool isGroupCollapsed(const QString &groupName);
|
||||||
@ -102,6 +113,9 @@ public:
|
|||||||
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
void setInstanceGroup(const InstanceId & id, const GroupId& name);
|
||||||
|
|
||||||
void deleteGroup(const GroupId & name);
|
void deleteGroup(const GroupId & name);
|
||||||
|
bool trashInstance(const InstanceId &id);
|
||||||
|
bool trashedSomething();
|
||||||
|
void undoTrashInstance();
|
||||||
void deleteInstance(const InstanceId & id);
|
void deleteInstance(const InstanceId & id);
|
||||||
|
|
||||||
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
// Wrap an instance creation task in some more task machinery and make it ready to be used
|
||||||
@ -116,8 +130,10 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
* Commit the staging area given by @keyPath to the provider - used when creation succeeds.
|
||||||
* Used by instance manipulation tasks.
|
* Used by instance manipulation tasks.
|
||||||
|
* should_override is used when another similar instance already exists, and we want to override it
|
||||||
|
* - for instance, when updating it.
|
||||||
*/
|
*/
|
||||||
bool commitStagedInstance(const QString & keyPath, const QString& instanceName, const QString & groupName);
|
bool commitStagedInstance(const QString& keyPath, const InstanceName& instanceName, const QString& groupName, const InstanceTask&);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
* Destroy a previously created staging area given by @keyPath - used when creation fails.
|
||||||
@ -138,6 +154,8 @@ public:
|
|||||||
QStringList mimeTypes() const override;
|
QStringList mimeTypes() const override;
|
||||||
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
QMimeData *mimeData(const QModelIndexList &indexes) const override;
|
||||||
|
|
||||||
|
QStringList getLinkedInstancesById(const QString &id) const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dataIsInvalid();
|
void dataIsInvalid();
|
||||||
void instancesChanged();
|
void instancesChanged();
|
||||||
@ -180,4 +198,6 @@ private:
|
|||||||
QSet<InstanceId> instanceSet;
|
QSet<InstanceId> instanceSet;
|
||||||
bool m_groupsLoaded = false;
|
bool m_groupsLoaded = false;
|
||||||
bool m_instancesProbed = false;
|
bool m_instancesProbed = false;
|
||||||
|
|
||||||
|
QStack<TrashHistoryItem> m_trashHistory;
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "ui/pages/BasePageProvider.h"
|
#include "ui/pages/BasePageProvider.h"
|
||||||
#include "ui/pages/instance/LogPage.h"
|
#include "ui/pages/instance/LogPage.h"
|
||||||
#include "ui/pages/instance/VersionPage.h"
|
#include "ui/pages/instance/VersionPage.h"
|
||||||
|
#include "ui/pages/instance/ManagedPackPage.h"
|
||||||
#include "ui/pages/instance/ModFolderPage.h"
|
#include "ui/pages/instance/ModFolderPage.h"
|
||||||
#include "ui/pages/instance/ResourcePackPage.h"
|
#include "ui/pages/instance/ResourcePackPage.h"
|
||||||
#include "ui/pages/instance/TexturePackPage.h"
|
#include "ui/pages/instance/TexturePackPage.h"
|
||||||
@ -33,13 +34,15 @@ public:
|
|||||||
values.append(new LogPage(inst));
|
values.append(new LogPage(inst));
|
||||||
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
std::shared_ptr<MinecraftInstance> onesix = std::dynamic_pointer_cast<MinecraftInstance>(inst);
|
||||||
values.append(new VersionPage(onesix.get()));
|
values.append(new VersionPage(onesix.get()));
|
||||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Mods"), "Loader-mods");
|
values.append(ManagedPackPage::createPage(onesix.get()));
|
||||||
modsPage->setFilter("%1 (*.zip *.jar *.litemod)");
|
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList());
|
||||||
|
modsPage->setFilter("%1 (*.zip *.jar *.litemod *.nilmod)");
|
||||||
values.append(modsPage);
|
values.append(modsPage);
|
||||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
|
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList()));
|
||||||
values.append(new ResourcePackPage(onesix.get()));
|
values.append(new NilModFolderPage(onesix.get(), onesix->nilModList()));
|
||||||
values.append(new TexturePackPage(onesix.get()));
|
values.append(new ResourcePackPage(onesix.get(), onesix->resourcePackList()));
|
||||||
values.append(new ShaderPackPage(onesix.get()));
|
values.append(new TexturePackPage(onesix.get(), onesix->texturePackList()));
|
||||||
|
values.append(new ShaderPackPage(onesix.get(), onesix->shaderPackList()));
|
||||||
values.append(new NotesPage(onesix.get()));
|
values.append(new NotesPage(onesix.get()));
|
||||||
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
values.append(new WorldListPage(onesix.get(), onesix->worldList()));
|
||||||
values.append(new ServersPage(onesix));
|
values.append(new ServersPage(onesix));
|
||||||
|
@ -1,9 +1,75 @@
|
|||||||
#include "InstanceTask.h"
|
#include "InstanceTask.h"
|
||||||
|
|
||||||
InstanceTask::InstanceTask()
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
|
|
||||||
|
InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name)
|
||||||
{
|
{
|
||||||
|
auto dialog =
|
||||||
|
CustomMessageBox::selectable(parent, QObject::tr("Change instance name"),
|
||||||
|
QObject::tr("The instance's name seems to include the old version. Would you like to update it?\n\n"
|
||||||
|
"Old name: %1\n"
|
||||||
|
"New name: %2")
|
||||||
|
.arg(old_name, new_name),
|
||||||
|
QMessageBox::Question, QMessageBox::No | QMessageBox::Yes);
|
||||||
|
auto result = dialog->exec();
|
||||||
|
|
||||||
|
if (result == QMessageBox::Yes)
|
||||||
|
return InstanceNameChange::ShouldChange;
|
||||||
|
return InstanceNameChange::ShouldKeep;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceTask::~InstanceTask()
|
ShouldUpdate askIfShouldUpdate(QWidget *parent, QString original_version_name)
|
||||||
{
|
{
|
||||||
|
auto info = CustomMessageBox::selectable(
|
||||||
|
parent, QObject::tr("Similar modpack was found!"),
|
||||||
|
QObject::tr("One or more of your instances are from this same modpack%1. Do you want to create a "
|
||||||
|
"separate instance, or update the existing one?\n\nNOTE: Make sure you made a backup of your important instance data before "
|
||||||
|
"updating, as worlds can be corrupted and some configuration may be lost (due to pack overrides).")
|
||||||
|
.arg(original_version_name),
|
||||||
|
QMessageBox::Information, QMessageBox::Ok | QMessageBox::Reset | QMessageBox::Abort);
|
||||||
|
info->setButtonText(QMessageBox::Ok, QObject::tr("Update existing instance"));
|
||||||
|
info->setButtonText(QMessageBox::Abort, QObject::tr("Create new instance"));
|
||||||
|
info->setButtonText(QMessageBox::Reset, QObject::tr("Cancel"));
|
||||||
|
|
||||||
|
info->exec();
|
||||||
|
|
||||||
|
if (info->clickedButton() == info->button(QMessageBox::Ok))
|
||||||
|
return ShouldUpdate::Update;
|
||||||
|
if (info->clickedButton() == info->button(QMessageBox::Abort))
|
||||||
|
return ShouldUpdate::SkipUpdating;
|
||||||
|
return ShouldUpdate::Cancel;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString InstanceName::name() const
|
||||||
|
{
|
||||||
|
if (!m_modified_name.isEmpty())
|
||||||
|
return modifiedName();
|
||||||
|
return QString("%1 %2").arg(m_original_name, m_original_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString InstanceName::originalName() const
|
||||||
|
{
|
||||||
|
return m_original_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString InstanceName::modifiedName() const
|
||||||
|
{
|
||||||
|
if (!m_modified_name.isEmpty())
|
||||||
|
return m_modified_name;
|
||||||
|
return m_original_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString InstanceName::version() const
|
||||||
|
{
|
||||||
|
return m_original_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstanceName::setName(InstanceName& other)
|
||||||
|
{
|
||||||
|
m_original_name = other.m_original_name;
|
||||||
|
m_original_version = other.m_original_version;
|
||||||
|
m_modified_name = other.m_modified_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
InstanceTask::InstanceTask() : Task(), InstanceName() {}
|
||||||
|
@ -1,52 +1,72 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "tasks/Task.h"
|
|
||||||
#include "settings/SettingsObject.h"
|
#include "settings/SettingsObject.h"
|
||||||
|
#include "tasks/Task.h"
|
||||||
|
|
||||||
class InstanceTask : public Task
|
/* Helpers */
|
||||||
{
|
enum class InstanceNameChange { ShouldChange, ShouldKeep };
|
||||||
|
[[nodiscard]] InstanceNameChange askForChangingInstanceName(QWidget* parent, const QString& old_name, const QString& new_name);
|
||||||
|
enum class ShouldUpdate { Update, SkipUpdating, Cancel };
|
||||||
|
[[nodiscard]] ShouldUpdate askIfShouldUpdate(QWidget* parent, QString original_version_name);
|
||||||
|
|
||||||
|
struct InstanceName {
|
||||||
|
public:
|
||||||
|
InstanceName() = default;
|
||||||
|
InstanceName(QString name, QString version) : m_original_name(std::move(name)), m_original_version(std::move(version)) {}
|
||||||
|
|
||||||
|
[[nodiscard]] QString modifiedName() const;
|
||||||
|
[[nodiscard]] QString originalName() const;
|
||||||
|
[[nodiscard]] QString name() const;
|
||||||
|
[[nodiscard]] QString version() const;
|
||||||
|
|
||||||
|
void setName(QString name) { m_modified_name = name; }
|
||||||
|
void setName(InstanceName& other);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QString m_original_name;
|
||||||
|
QString m_original_version;
|
||||||
|
|
||||||
|
QString m_modified_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InstanceTask : public Task, public InstanceName {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit InstanceTask();
|
InstanceTask();
|
||||||
virtual ~InstanceTask();
|
~InstanceTask() override = default;
|
||||||
|
|
||||||
void setParentSettings(SettingsObjectPtr settings)
|
void setParentSettings(SettingsObjectPtr settings) { m_globalSettings = settings; }
|
||||||
{
|
|
||||||
m_globalSettings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setStagingPath(const QString &stagingPath)
|
void setStagingPath(const QString& stagingPath) { m_stagingPath = stagingPath; }
|
||||||
{
|
|
||||||
m_stagingPath = stagingPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setName(const QString &name)
|
void setIcon(const QString& icon) { m_instIcon = icon; }
|
||||||
{
|
|
||||||
m_instName = name;
|
|
||||||
}
|
|
||||||
QString name() const
|
|
||||||
{
|
|
||||||
return m_instName;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setIcon(const QString &icon)
|
void setGroup(const QString& group) { m_instGroup = group; }
|
||||||
{
|
QString group() const { return m_instGroup; }
|
||||||
m_instIcon = icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGroup(const QString &group)
|
[[nodiscard]] bool shouldConfirmUpdate() const { return m_confirm_update; }
|
||||||
|
void setConfirmUpdate(bool confirm) { m_confirm_update = confirm; }
|
||||||
|
|
||||||
|
bool shouldOverride() const { return m_override_existing; }
|
||||||
|
|
||||||
|
[[nodiscard]] QString originalInstanceID() const { return m_original_instance_id; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setOverride(bool override, QString instance_id_to_override = {})
|
||||||
{
|
{
|
||||||
m_instGroup = group;
|
m_override_existing = override;
|
||||||
}
|
if (!instance_id_to_override.isEmpty())
|
||||||
QString group() const
|
m_original_instance_id = instance_id_to_override;
|
||||||
{
|
|
||||||
return m_instGroup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected: /* data */
|
protected: /* data */
|
||||||
SettingsObjectPtr m_globalSettings;
|
SettingsObjectPtr m_globalSettings;
|
||||||
QString m_instName;
|
|
||||||
QString m_instIcon;
|
QString m_instIcon;
|
||||||
QString m_instGroup;
|
QString m_instGroup;
|
||||||
QString m_stagingPath;
|
QString m_stagingPath;
|
||||||
|
|
||||||
|
bool m_override_existing = false;
|
||||||
|
bool m_confirm_update = true;
|
||||||
|
|
||||||
|
QString m_original_instance_id;
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,47 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "JavaCommon.h"
|
#include "JavaCommon.h"
|
||||||
|
#include "java/JavaUtils.h"
|
||||||
#include "ui/dialogs/CustomMessageBox.h"
|
#include "ui/dialogs/CustomMessageBox.h"
|
||||||
#include <MMCStrings.h>
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||||
{
|
{
|
||||||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegExp("-Xm[sx]"))
|
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegularExpression("-Xm[sx]"))
|
||||||
|| jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize"))
|
|| jvmargs.contains("-XX-MaxHeapSize") || jvmargs.contains("-XX:InitialHeapSize"))
|
||||||
{
|
{
|
||||||
auto warnStr = QObject::tr(
|
auto warnStr = QObject::tr(
|
||||||
@ -17,6 +54,17 @@ bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
|||||||
QMessageBox::Warning)->exec();
|
QMessageBox::Warning)->exec();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// block lunacy with passing required version to the JVM
|
||||||
|
if (jvmargs.contains(QRegularExpression("-version:.*"))) {
|
||||||
|
auto warnStr = QObject::tr(
|
||||||
|
"You tried to pass required Java version argument to the JVM (using \"-version:xxx\"). This is not safe and will not be allowed.\n"
|
||||||
|
"This message will be displayed until you remove this from the JVM arguments.");
|
||||||
|
CustomMessageBox::selectable(
|
||||||
|
parent, QObject::tr("JVM arguments warning"),
|
||||||
|
warnStr,
|
||||||
|
QMessageBox::Warning)->exec();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +88,7 @@ void JavaCommon::javaArgsWereBad(QWidget *parent, JavaCheckResult result)
|
|||||||
auto htmlError = result.errorLog;
|
auto htmlError = result.errorLog;
|
||||||
QString text;
|
QString text;
|
||||||
htmlError.replace('\n', "<br />");
|
htmlError.replace('\n', "<br />");
|
||||||
text += QObject::tr("The specified java binary didn't work with the arguments you provided:<br />");
|
text += QObject::tr("The specified Java binary didn't work with the arguments you provided:<br />");
|
||||||
text += QString("<font color=\"red\">%1</font>").arg(htmlError);
|
text += QString("<font color=\"red\">%1</font>").arg(htmlError);
|
||||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||||
}
|
}
|
||||||
@ -49,8 +97,15 @@ void JavaCommon::javaBinaryWasBad(QWidget *parent, JavaCheckResult result)
|
|||||||
{
|
{
|
||||||
QString text;
|
QString text;
|
||||||
text += QObject::tr(
|
text += QObject::tr(
|
||||||
"The specified java binary didn't work.<br />You should use the auto-detect feature, "
|
"The specified Java binary didn't work.<br />You should use the auto-detect feature, "
|
||||||
"or set the path to the java executable.<br />");
|
"or set the path to the Java executable.<br />");
|
||||||
|
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JavaCommon::javaCheckNotFound(QWidget *parent)
|
||||||
|
{
|
||||||
|
QString text;
|
||||||
|
text += QObject::tr("Java checker library could not be found. Please check your installation.");
|
||||||
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
CustomMessageBox::selectable(parent, QObject::tr("Java test failure"), text, QMessageBox::Warning)->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,9 +116,13 @@ void JavaCommon::TestCheck::run()
|
|||||||
emit finished();
|
emit finished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (JavaUtils::getJavaCheckPath().isEmpty()) {
|
||||||
|
javaCheckNotFound(m_parent);
|
||||||
|
emit finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker());
|
||||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinished);
|
||||||
SLOT(checkFinished(JavaCheckResult)));
|
|
||||||
checker->m_path = m_path;
|
checker->m_path = m_path;
|
||||||
checker->performCheck();
|
checker->performCheck();
|
||||||
}
|
}
|
||||||
@ -77,8 +136,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checker.reset(new JavaChecker());
|
checker.reset(new JavaChecker());
|
||||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
connect(checker.get(), &JavaChecker::checkFinished, this, &JavaCommon::TestCheck::checkFinishedWithArgs);
|
||||||
SLOT(checkFinishedWithArgs(JavaCheckResult)));
|
|
||||||
checker->m_path = m_path;
|
checker->m_path = m_path;
|
||||||
checker->m_args = m_args;
|
checker->m_args = m_args;
|
||||||
checker->m_minMem = m_minMem;
|
checker->m_minMem = m_minMem;
|
||||||
|
@ -10,12 +10,14 @@ namespace JavaCommon
|
|||||||
{
|
{
|
||||||
bool checkJVMArgs(QString args, QWidget *parent);
|
bool checkJVMArgs(QString args, QWidget *parent);
|
||||||
|
|
||||||
// Show a dialog saying that the Java binary was not usable
|
|
||||||
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result);
|
|
||||||
// Show a dialog saying that the Java binary was not usable because of bad options
|
|
||||||
void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
|
|
||||||
// Show a dialog saying that the Java binary was usable
|
// Show a dialog saying that the Java binary was usable
|
||||||
void javaWasOk(QWidget *parent, JavaCheckResult result);
|
void javaWasOk(QWidget *parent, JavaCheckResult result);
|
||||||
|
// Show a dialog saying that the Java binary was not usable because of bad options
|
||||||
|
void javaArgsWereBad(QWidget *parent, JavaCheckResult result);
|
||||||
|
// Show a dialog saying that the Java binary was not usable
|
||||||
|
void javaBinaryWasBad(QWidget *parent, JavaCheckResult result);
|
||||||
|
// Show a dialog if we couldn't find Java Checker
|
||||||
|
void javaCheckNotFound(QWidget *parent);
|
||||||
|
|
||||||
class TestCheck : public QObject
|
class TestCheck : public QObject
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,37 @@
|
|||||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
|
|
||||||
@ -22,14 +55,6 @@ void write(const QJsonArray &array, const QString &filename)
|
|||||||
write(QJsonDocument(array), filename);
|
write(QJsonDocument(array), filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray toBinary(const QJsonObject &obj)
|
|
||||||
{
|
|
||||||
return QJsonDocument(obj).toBinaryData();
|
|
||||||
}
|
|
||||||
QByteArray toBinary(const QJsonArray &array)
|
|
||||||
{
|
|
||||||
return QJsonDocument(array).toBinaryData();
|
|
||||||
}
|
|
||||||
QByteArray toText(const QJsonObject &obj)
|
QByteArray toText(const QJsonObject &obj)
|
||||||
{
|
{
|
||||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||||
@ -48,12 +73,8 @@ QJsonDocument requireDocument(const QByteArray &data, const QString &what)
|
|||||||
{
|
{
|
||||||
if (isBinaryJson(data))
|
if (isBinaryJson(data))
|
||||||
{
|
{
|
||||||
QJsonDocument doc = QJsonDocument::fromBinaryData(data);
|
// FIXME: Is this needed?
|
||||||
if (doc.isNull())
|
throw JsonException(what + ": Invalid JSON. Binary JSON unsupported");
|
||||||
{
|
|
||||||
throw JsonException(what + ": Invalid JSON (binary JSON detected)");
|
|
||||||
}
|
|
||||||
return doc;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,37 @@
|
|||||||
// Licensed under the Apache-2.0 license. See README.md for details.
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@ -29,8 +62,6 @@ void write(const QJsonObject &object, const QString &filename);
|
|||||||
/// @throw FileSystemException
|
/// @throw FileSystemException
|
||||||
void write(const QJsonArray &array, const QString &filename);
|
void write(const QJsonArray &array, const QString &filename);
|
||||||
|
|
||||||
QByteArray toBinary(const QJsonObject &obj);
|
|
||||||
QByteArray toBinary(const QJsonArray &array);
|
|
||||||
QByteArray toText(const QJsonObject &obj);
|
QByteArray toText(const QJsonObject &obj);
|
||||||
QByteArray toText(const QJsonArray &array);
|
QByteArray toText(const QJsonArray &array);
|
||||||
|
|
||||||
|
@ -1,3 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "LaunchController.h"
|
#include "LaunchController.h"
|
||||||
#include "minecraft/auth/AccountList.h"
|
#include "minecraft/auth/AccountList.h"
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
@ -36,7 +71,10 @@ void LaunchController::executeTask()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget);
|
if(!JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget)) {
|
||||||
|
emitFailed(tr("Invalid Java arguments specified. Please fix this first."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
login();
|
login();
|
||||||
}
|
}
|
||||||
@ -55,8 +93,8 @@ void LaunchController::decideAccount()
|
|||||||
auto reply = CustomMessageBox::selectable(
|
auto reply = CustomMessageBox::selectable(
|
||||||
m_parentWidget,
|
m_parentWidget,
|
||||||
tr("No Accounts"),
|
tr("No Accounts"),
|
||||||
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
|
tr("In order to play Minecraft, you must have at least one Microsoft or Mojang "
|
||||||
"account logged in. "
|
"account logged in. Mojang accounts can only be used offline. "
|
||||||
"Would you like to open the account manager to add an account now?"),
|
"Would you like to open the account manager to add an account now?"),
|
||||||
QMessageBox::Information,
|
QMessageBox::Information,
|
||||||
QMessageBox::Yes | QMessageBox::No
|
QMessageBox::Yes | QMessageBox::No
|
||||||
@ -67,9 +105,22 @@ void LaunchController::decideAccount()
|
|||||||
// Open the account manager.
|
// Open the account manager.
|
||||||
APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts");
|
APPLICATION->ShowGlobalSettings(m_parentWidget, "accounts");
|
||||||
}
|
}
|
||||||
|
else if (reply == QMessageBox::No)
|
||||||
|
{
|
||||||
|
// Do not open "profile select" dialog.
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Select the account to use. If the instance has a specific account set, that will be used. Otherwise, the default account will be used
|
||||||
|
auto instanceAccountId = m_instance->settings()->get("InstanceAccountId").toString();
|
||||||
|
auto instanceAccountIndex = accounts->findAccountByProfileId(instanceAccountId);
|
||||||
|
if (instanceAccountIndex == -1) {
|
||||||
m_accountToUse = accounts->defaultAccount();
|
m_accountToUse = accounts->defaultAccount();
|
||||||
|
} else {
|
||||||
|
m_accountToUse = accounts->at(instanceAccountIndex);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_accountToUse)
|
if (!m_accountToUse)
|
||||||
{
|
{
|
||||||
// If no default account is set, ask the user which one to use.
|
// If no default account is set, ask the user which one to use.
|
||||||
@ -102,18 +153,29 @@ void LaunchController::login() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we try empty password first :)
|
|
||||||
QString password;
|
|
||||||
// we loop until the user succeeds in logging in or gives up
|
// we loop until the user succeeds in logging in or gives up
|
||||||
bool tryagain = true;
|
bool tryagain = true;
|
||||||
// the failure. the default failure.
|
unsigned int tries = 0;
|
||||||
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter your password to log in again. <br /> <br /> This could be caused by a password change.");
|
|
||||||
QString failReason = needLoginAgain;
|
|
||||||
|
|
||||||
while (tryagain)
|
while (tryagain)
|
||||||
{
|
{
|
||||||
|
if (tries > 0 && tries % 3 == 0) {
|
||||||
|
auto result = QMessageBox::question(
|
||||||
|
m_parentWidget,
|
||||||
|
tr("Continue launch?"),
|
||||||
|
tr("It looks like we couldn't launch after %1 tries. Do you want to continue trying?")
|
||||||
|
.arg(tries)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == QMessageBox::No) {
|
||||||
|
emitAborted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tries++;
|
||||||
m_session = std::make_shared<AuthSession>();
|
m_session = std::make_shared<AuthSession>();
|
||||||
m_session->wants_online = m_online;
|
m_session->wants_online = m_online;
|
||||||
|
m_session->demo = m_demo;
|
||||||
m_accountToUse->fillSession(m_session);
|
m_accountToUse->fillSession(m_session);
|
||||||
|
|
||||||
// Launch immediately in true offline mode
|
// Launch immediately in true offline mode
|
||||||
@ -131,13 +193,20 @@ void LaunchController::login() {
|
|||||||
if(!m_session->wants_online) {
|
if(!m_session->wants_online) {
|
||||||
// we ask the user for a player name
|
// we ask the user for a player name
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
QString usedname = m_session->player_name;
|
|
||||||
|
QString message = tr("Choose your offline mode player name.");
|
||||||
|
if(m_session->demo) {
|
||||||
|
message = tr("Choose your demo mode player name.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString lastOfflinePlayerName = APPLICATION->settings()->get("LastOfflinePlayerName").toString();
|
||||||
|
QString usedname = lastOfflinePlayerName.isEmpty() ? m_session->player_name : lastOfflinePlayerName;
|
||||||
QString name = QInputDialog::getText(
|
QString name = QInputDialog::getText(
|
||||||
m_parentWidget,
|
m_parentWidget,
|
||||||
tr("Player name"),
|
tr("Player name"),
|
||||||
tr("Choose your offline mode player name."),
|
message,
|
||||||
QLineEdit::Normal,
|
QLineEdit::Normal,
|
||||||
m_session->player_name,
|
usedname,
|
||||||
&ok
|
&ok
|
||||||
);
|
);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
@ -148,6 +217,7 @@ void LaunchController::login() {
|
|||||||
if (name.length())
|
if (name.length())
|
||||||
{
|
{
|
||||||
usedname = name;
|
usedname = name;
|
||||||
|
APPLICATION->settings()->set("LastOfflinePlayerName", usedname);
|
||||||
}
|
}
|
||||||
m_session->MakeOffline(usedname);
|
m_session->MakeOffline(usedname);
|
||||||
// offline flavored game from here :3
|
// offline flavored game from here :3
|
||||||
@ -312,15 +382,15 @@ void LaunchController::launchInstance()
|
|||||||
}
|
}
|
||||||
resolved_servers = resolved_servers + "]\n\n";
|
resolved_servers = resolved_servers + "]\n\n";
|
||||||
}
|
}
|
||||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), resolved_servers, MessageLevel::Launcher));
|
||||||
} else {
|
} else {
|
||||||
online_mode = "offline";
|
online_mode = m_demo ? "demo" : "offline";
|
||||||
}
|
}
|
||||||
|
|
||||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), "Launched instance in " + online_mode + " mode\n", MessageLevel::Launcher));
|
||||||
|
|
||||||
// Prepend Version
|
// Prepend Version
|
||||||
m_launcher->prependStep(new TextPrint(m_launcher.get(), BuildConfig.LAUNCHER_NAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
m_launcher->prependStep(makeShared<TextPrint>(m_launcher.get(), BuildConfig.LAUNCHER_DISPLAYNAME + " version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::Launcher));
|
||||||
m_launcher->start();
|
m_launcher->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* PolyMC - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <BaseInstance.h>
|
#include <BaseInstance.h>
|
||||||
@ -28,6 +63,10 @@ public:
|
|||||||
m_online = online;
|
m_online = online;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setDemo(bool demo) {
|
||||||
|
m_demo = demo;
|
||||||
|
}
|
||||||
|
|
||||||
void setProfiler(BaseProfilerFactory *profiler) {
|
void setProfiler(BaseProfilerFactory *profiler) {
|
||||||
m_profiler = profiler;
|
m_profiler = profiler;
|
||||||
}
|
}
|
||||||
@ -66,6 +105,7 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
BaseProfilerFactory *m_profiler = nullptr;
|
BaseProfilerFactory *m_profiler = nullptr;
|
||||||
bool m_online = true;
|
bool m_online = true;
|
||||||
|
bool m_demo = false;
|
||||||
InstancePtr m_instance;
|
InstancePtr m_instance;
|
||||||
QWidget * m_parentWidget = nullptr;
|
QWidget * m_parentWidget = nullptr;
|
||||||
InstanceWindow *m_console = nullptr;
|
InstanceWindow *m_console = nullptr;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# Basic start script for running the launcher with the libs packaged with it.
|
# Basic start script for running the launcher with the libs packaged with it.
|
||||||
|
|
||||||
function printerror {
|
function printerror {
|
||||||
@ -18,13 +18,17 @@ LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
|
|||||||
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
|
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
|
||||||
echo "Launcher Dir: ${LAUNCHER_DIR}"
|
echo "Launcher Dir: ${LAUNCHER_DIR}"
|
||||||
|
|
||||||
# Set up env - filter out input LD_ variables but pass them in under different names
|
# Set up env.
|
||||||
export GAME_LIBRARY_PATH=${GAME_LIBRARY_PATH-${LD_LIBRARY_PATH}}
|
# Pass our custom variables separately so that the launcher can remove them for child processes
|
||||||
export GAME_PRELOAD=${GAME_PRELOAD-${LD_PRELOAD}}
|
export LAUNCHER_LD_LIBRARY_PATH="${LAUNCHER_DIR}/lib@LIB_SUFFIX@"
|
||||||
export LD_LIBRARY_PATH="${LAUNCHER_DIR}/bin":$LAUNCHER_LIBRARY_PATH
|
export LAUNCHER_LD_PRELOAD=""
|
||||||
export LD_PRELOAD=$LAUNCHER_PRELOAD
|
export LAUNCHER_QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
|
||||||
export QT_PLUGIN_PATH="${LAUNCHER_DIR}/plugins"
|
export LAUNCHER_QT_FONTPATH="${LAUNCHER_DIR}/fonts"
|
||||||
export QT_FONTPATH="${LAUNCHER_DIR}/fonts"
|
|
||||||
|
export LD_LIBRARY_PATH="$LAUNCHER_LD_LIBRARY_PATH:$LD_LIBRARY_PATH"
|
||||||
|
export LD_PRELOAD="$LAUNCHER_LD_PRELOAD:$LD_PRELOAD"
|
||||||
|
export QT_PLUGIN_PATH="$LAUNCHER_QT_PLUGIN_PATH:$QT_PLUGIN_PATH"
|
||||||
|
export QT_FONTPATH="$LAUNCHER_QT_FONTPATH:$QT_FONTPATH"
|
||||||
|
|
||||||
# Detect missing dependencies...
|
# Detect missing dependencies...
|
||||||
DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'`
|
DEPS_LIST=`ldd "${LAUNCHER_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'`
|
||||||
|
@ -1,14 +1,51 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
/*
|
||||||
|
* Prism Launcher - Minecraft Launcher
|
||||||
|
* Copyright (C) 2022,2023 Sefa Eyeoglu <contact@scrumplex.net>
|
||||||
|
* Copyright (c) 2023 flowln <flowlnlnln@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* This file incorporates work covered by the following copyright and
|
||||||
|
* permission notice:
|
||||||
|
*
|
||||||
|
* Copyright 2013-2021 MultiMC Contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "LoggedProcess.h"
|
#include "LoggedProcess.h"
|
||||||
#include "MessageLevel.h"
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QTextDecoder>
|
||||||
|
#include "MessageLevel.h"
|
||||||
|
|
||||||
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
|
LoggedProcess::LoggedProcess(QObject *parent) : QProcess(parent)
|
||||||
{
|
{
|
||||||
// QProcess has a strange interface... let's map a lot of those into a few.
|
// QProcess has a strange interface... let's map a lot of those into a few.
|
||||||
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
|
connect(this, &QProcess::readyReadStandardOutput, this, &LoggedProcess::on_stdOut);
|
||||||
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
|
connect(this, &QProcess::readyReadStandardError, this, &LoggedProcess::on_stdErr);
|
||||||
connect(this, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(on_exit(int,QProcess::ExitStatus)));
|
connect(this, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &LoggedProcess::on_exit);
|
||||||
connect(this, SIGNAL(error(QProcess::ProcessError)), this, SLOT(on_error(QProcess::ProcessError)));
|
connect(this, &QProcess::errorOccurred, this, &LoggedProcess::on_error);
|
||||||
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
|
connect(this, &QProcess::stateChanged, this, &LoggedProcess::on_stateChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,25 +57,35 @@ LoggedProcess::~LoggedProcess()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList reprocess(const QByteArray & data, QString & leftover)
|
QStringList LoggedProcess::reprocess(const QByteArray& data, QTextDecoder& decoder)
|
||||||
{
|
{
|
||||||
QString str = leftover + QString::fromLocal8Bit(data);
|
auto str = decoder.toUnicode(data);
|
||||||
|
|
||||||
str.remove('\r');
|
if (!m_leftover_line.isEmpty()) {
|
||||||
QStringList lines = str.split("\n");
|
str.prepend(m_leftover_line);
|
||||||
leftover = lines.takeLast();
|
m_leftover_line = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, QString::SkipEmptyParts);
|
||||||
|
#else
|
||||||
|
auto lines = str.remove(QChar::CarriageReturn).split(QChar::LineFeed, Qt::SkipEmptyParts);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!str.endsWith(QChar::LineFeed))
|
||||||
|
m_leftover_line = lines.takeLast();
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoggedProcess::on_stdErr()
|
void LoggedProcess::on_stdErr()
|
||||||
{
|
{
|
||||||
auto lines = reprocess(readAllStandardError(), m_err_leftover);
|
auto lines = reprocess(readAllStandardError(), m_err_decoder);
|
||||||
emit log(lines, MessageLevel::StdErr);
|
emit log(lines, MessageLevel::StdErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoggedProcess::on_stdOut()
|
void LoggedProcess::on_stdOut()
|
||||||
{
|
{
|
||||||
auto lines = reprocess(readAllStandardOutput(), m_out_leftover);
|
auto lines = reprocess(readAllStandardOutput(), m_out_decoder);
|
||||||
emit log(lines, MessageLevel::StdOut);
|
emit log(lines, MessageLevel::StdOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,18 +94,6 @@ void LoggedProcess::on_exit(int exit_code, QProcess::ExitStatus status)
|
|||||||
// save the exit code
|
// save the exit code
|
||||||
m_exit_code = exit_code;
|
m_exit_code = exit_code;
|
||||||
|
|
||||||
// Flush console window
|
|
||||||
if (!m_err_leftover.isEmpty())
|
|
||||||
{
|
|
||||||
emit log({m_err_leftover}, MessageLevel::StdErr);
|
|
||||||
m_err_leftover.clear();
|
|
||||||
}
|
|
||||||
if (!m_out_leftover.isEmpty())
|
|
||||||
{
|
|
||||||
emit log({m_err_leftover}, MessageLevel::StdOut);
|
|
||||||
m_out_leftover.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// based on state, send signals
|
// based on state, send signals
|
||||||
if (!m_is_aborting)
|
if (!m_is_aborting)
|
||||||
{
|
{
|
||||||
@ -157,19 +192,6 @@ void LoggedProcess::on_stateChange(QProcess::ProcessState state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined Q_OS_WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
qint64 LoggedProcess::processId() const
|
|
||||||
{
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
return pid() ? pid()->dwProcessId : 0;
|
|
||||||
#else
|
|
||||||
return pid();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoggedProcess::setDetachable(bool detachable)
|
void LoggedProcess::setDetachable(bool detachable)
|
||||||
{
|
{
|
||||||
m_is_detachable = detachable;
|
m_is_detachable = detachable;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user