Compare commits

...

462 Commits

Author SHA1 Message Date
55f811af62 Merge remote-tracking branch 'origin/release/v9.4.0' into develop 2026-03-27 09:16:10 +03:00
057f0bbbf6 Add scripts for thumbnail for site 2026-03-26 16:10:46 +03:00
67060cc66e Merge pull request 'master' (#177) from master into release/v9.4.0 2026-03-24 08:47:58 +00:00
c5f6c2e02b Merge pull request 'fix/js-doc' (#176) from fix/js-doc into master 2026-03-24 08:45:33 +00:00
71b912e7e6 [jsdoc] Added missed calls get_translation method 2026-03-24 15:31:23 +07:00
6e7fd50583 [jsdoc] Refactor resolve @link tag 2026-03-24 13:39:38 +07:00
cf39498098 [jsdoc] Don't use default path for translations 2026-03-24 13:37:32 +07:00
1f4e593943 fix readme 2026-03-19 12:55:28 +03:00
3dfd22c735 [jsdoc] Fix lang for reserved link 2026-03-19 15:02:25 +07:00
f92fc0f617 Merge branch hotfix/v9.3.1 into release/v9.4.0 2026-03-12 15:16:28 +00:00
0402a5a07a Merge branch hotfix/v9.3.1 into develop 2026-03-12 15:16:27 +00:00
dea91ca6f6 Merge branch hotfix/v9.3.1 into master 2026-03-12 15:16:26 +00:00
4be0e0cbe2 [jsdoc] Fix write missed/unused translations 2026-03-11 16:32:11 +00:00
1e8825e15e [develop] Improve README with fixes and Git Bash note 2026-03-11 19:30:43 +03:00
9d17f87811 fix/js-doc (#174)
Add translations
Co-authored-by: Nikita Khromov <Nikita.Khromov@onlyoffice.com>
2026-03-11 09:24:59 +00:00
2fff3a7391 [develop] Skip empty addon dirs and log npm steps in build_server_with_addons 2026-03-11 01:35:21 +03:00
c0bdb1d62b [socketio] Up module version 2026-03-09 21:15:54 +03:00
7c97a9b326 Merge branch hotfix/v9.3.1 into master 2026-03-05 07:44:16 +00:00
b33d92a32e Merge pull request 'Rework Dockerfile for Ubuntu 24.04 base image' (#173) from fix/dockerfile into hotfix/v9.3.1
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/173
2026-03-04 13:43:26 +00:00
d480266a5a Rework Dockerfile for Ubuntu 24.04 base image 2026-03-04 18:37:55 +05:00
98af1ed74b Merge pull request 'release/v9.3.0' (#172) from release/v9.3.0 into master 2026-03-03 17:53:56 +00:00
9428ce8b33 Merge branch hotfix/v9.3.1 into master 2026-03-03 11:55:43 +00:00
c1dbdc39f1 Merge pull request 'Up version 9.3.1' (#171) from fix/version9.3.1 into hotfix/v9.3.1
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/171
2026-03-03 09:45:04 +00:00
5d99680cc4 Up version 9.3.1 2026-03-03 14:36:02 +05:00
930b11f19e Merge pull request '[jsdoc] Fix prev' (#170) from fix/js-doc into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/170
2026-02-27 10:33:00 +00:00
62aa75d82c [jsdoc] Fix prev 2026-02-27 17:32:02 +07:00
f926970677 Merge pull request '[jsdoc] Fix generate paths for enumerations' (#169) from fix/js-doc into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/169
2026-02-26 11:35:50 +00:00
4f6908154c [jsdoc] Fix generate paths for enumerations 2026-02-26 18:34:03 +07:00
151c4e7d2f Merge branch release/v9.3.0 into develop 2026-02-25 15:13:49 +00:00
8adc9021e4 Merge branch release/v9.3.0 into master 2026-02-25 15:13:48 +00:00
9b97de22df Merge pull request '[jsdoc] Fix editor name' (#168) from fix/js-doc into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/168
2026-02-25 08:59:34 +00:00
997734860b [jsdoc] Fix editor name 2026-02-25 15:53:24 +07:00
4473fd7cf9 Merge branch release/v9.3.0 into master 2026-02-24 14:05:39 +00:00
923d839483 [build] Reapply server-admin-panel addon check before building admin panel 2026-02-24 11:38:28 +03:00
39b1c1e22c [server] Build adminpanel from server-admin-panel private repository 2026-02-24 11:38:28 +03:00
849d78fea0 [build] Reapply server-admin-panel addon check before building admin panel 2026-02-22 23:21:43 +03:00
49619cdb40 Merge pull request '[jsdoc] rename forms editor to "forms" instead of "pdf"' (#167) from fix/js-doc into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/167
2026-02-22 10:59:54 +00:00
9bdb69dfa3 [jsdoc] rename forms editor to "forms" instead of "pdf" 2026-02-22 17:53:21 +07:00
ceda5ea658 Merge pull request '[jsdoc] Api Docs for PDF editor' (#166) from fix/js-doc into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/166
2026-02-22 10:50:06 +00:00
ec2d587993 [jsdoc] Api Docs for PDF editor 2026-02-22 17:40:41 +07:00
10118f4c68 Revert "Check server-admin-panel addon before building admin panel"
This reverts commit 3cdc164d7f.
2026-02-20 23:42:39 +03:00
3cdc164d7f Check server-admin-panel addon before building admin panel 2026-02-20 20:11:36 +03:00
b9c9811b9e [server] Build adminpanel from server-admin-panel private repository 2026-02-19 17:43:40 +03:00
70bbdfbd43 Fix build 2026-02-19 11:27:54 +03:00
5dbf27a039 Fix build 2026-02-18 16:54:53 +03:00
39fb488af8 Merge remote-tracking branch 'origin/release/v9.3.0' into develop 2026-02-17 22:39:07 +03:00
4866786097 Merge pull request 'Up version to 9.3.0' (#164) from fix/version9.3.0 into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/164
2026-02-16 07:39:53 +00:00
07b1eadc0a Up version to 9.3.0 2026-02-16 10:21:01 +05:00
9db40b2505 Fix the uninitialized config version 2026-02-06 14:50:19 +03:00
42b57d6b40 Merge branch 'release/v9.3.0' into develop 2026-02-03 10:57:03 +03:00
506fbd056a Fix build 2026-01-29 13:41:32 +03:00
def11f3134 Fix build 2026-01-29 13:16:06 +03:00
3285a3e3c5 Fix clean build with sysroot 2026-01-29 12:23:45 +03:00
bdcdfa89e7 [server] Add document-formats repository to server deploy 2026-01-29 01:52:42 +03:00
1835e3ad28 Merge branch 'release/v9.3.0' of https://git.onlyoffice.com/ONLYOFFICE/build_tools into release/v9.3.0 2026-01-28 16:16:04 +03:00
1b33175880 Update iwork module 2026-01-28 16:15:53 +03:00
4629471d5b [deploy] Set docservice max_old_space_size=6144 for bug 75586 2026-01-28 14:00:44 +03:00
a4b29c6acc Fix build 2026-01-27 22:51:15 +03:00
621b34ffde Fix build 2026-01-27 19:23:17 +03:00
29ceaa34bf Update README.md 2026-01-22 23:40:46 +03:00
50addf9d18 Remove legacy desktop editors update build 2026-01-22 17:25:51 +03:00
7269dda75e Merge pull request 'Set build environment for swift compiling' (#161) from feature/ios-swiftc into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/161
2026-01-20 20:31:47 +00:00
3f54aec7f8 Add ooxmlsirnature ref to x2t 2026-01-20 20:57:50 +03:00
2d059101ee Set build environment for swift compiling 2026-01-20 17:08:08 +04:00
4dda7dfa7a Merge branch 'release/v9.3.0' into develop 2026-01-14 15:40:31 +03:00
b6d3392571 Fix package build paths 2026-01-13 18:21:00 +03:00
4001a636db Fix boost build (macos) 2026-01-12 17:01:23 +03:00
cf4c2fc02f [build] Pin @yao-pkg/pkg to version 6.11.0; For bug 76727 2025-12-30 17:17:58 +03:00
f94a95df22 Merge branch 'hotfix/v9.2.1' into release/v9.3.0 2025-12-28 15:07:46 +03:00
7cbaa00356 Merge branch hotfix/v9.2.1 into develop 2025-12-26 16:09:43 +00:00
71cd913944 Merge branch hotfix/v9.2.1 into master 2025-12-26 16:09:42 +00:00
76fd5a0d37 Merge pull request 'Add starmath lib to sln.json' (#158) from fix/build-starmath into release/v9.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/158
2025-12-26 11:43:21 +00:00
db1b2c01fc Add starmath lib to deploy 2025-12-26 15:40:03 +04:00
9cc6e0b749 Add starmath lib to sln.json 2025-12-26 14:44:12 +04:00
8912b82d1f Fix typo 2025-12-26 10:58:34 +03:00
d6e215595f Fix create whl package 2025-12-26 02:01:25 +03:00
6f59b470a3 Refactoring 2025-12-26 01:42:56 +03:00
9241aa8a9b Fix typo 2025-12-26 01:10:22 +03:00
87a7c2922b Remove symlinks in whl package 2025-12-26 00:46:05 +03:00
211348776c Fix icu build for mac_64 2025-12-26 00:37:12 +03:00
b2f6b6e2c3 Fix deploy macos builder 2025-12-25 23:50:17 +03:00
4ea37abdf3 Merge branch hotfix/v9.2.1 into master 2025-12-25 10:56:04 +00:00
ee324a764e Fix desktopeditors-help packages upload 2025-12-24 11:15:13 +03:00
cee13fbcbe Remove use sysroot for copy (generate) boost headers 2025-12-23 15:08:34 +03:00
453786b595 Remove debug commit 2025-12-22 20:08:01 +03:00
9eb7651e84 Merge branch 'hotfix/v9.2.1' of https://git.onlyoffice.com/ONLYOFFICE/build_tools into hotfix/v9.2.1 2025-12-22 19:55:17 +03:00
7b37cc9f83 Fix fetch sysroots 2025-12-22 19:55:07 +03:00
c2a38839dd Fix sysroot fetch command 2025-12-22 19:54:50 +03:00
be9f90d220 Fix deploy for cross arm builds 2025-12-22 12:25:21 +03:00
91937f3d77 Add instructions for linux setup 2025-12-22 06:05:35 +03:00
e70acf9822 Refactoring sysroots 2025-12-22 05:59:02 +03:00
243a14ddb9 Fix typo 2025-12-21 22:58:04 +03:00
fd7cf76aea Fix typo 2025-12-21 22:00:32 +03:00
e48534d14c Fix typo 2025-12-21 20:51:38 +03:00
055ff85f68 Fix typo 2025-12-21 18:46:25 +03:00
12a1765ca0 Fix v8.data path 2025-12-21 18:45:21 +03:00
763dea4889 Add script for fetch python 2025-12-21 17:03:55 +03:00
38a3863213 Fix typo 2025-12-21 16:31:07 +03:00
c860de3721 Add missed file 2025-12-21 15:43:18 +03:00
3ce6a41dce Refactoring sysroots 2025-12-21 15:11:47 +03:00
c69158af8f Refactoring fetch sysroots 2025-12-20 22:56:27 +03:00
b47b412529 Refactoring 2025-12-20 22:44:35 +03:00
355ae98932 Merge branch hotfix/v9.2.1 into release/v9.3.0 2025-12-17 15:25:36 +00:00
4d812fa6d2 Merge branch hotfix/v9.2.1 into develop 2025-12-17 15:25:35 +00:00
ef8153c053 Merge branch hotfix/v9.2.1 into master 2025-12-17 11:31:38 +00:00
430a9878e5 Merge pull request 'Up version to 9.2.1' (#156) from fix/version9.2.1 into hotfix/v9.2.1
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/156
2025-12-16 07:06:39 +00:00
561c648069 Up version to 9.2.1 2025-12-16 10:08:45 +05:00
f99bcd2ed0 Merge branch release/v9.2.0 into develop 2025-12-10 10:29:26 +00:00
df1c566005 Merge branch release/v9.2.0 into master 2025-12-10 10:29:24 +00:00
d548d731ae Update scripts/package_builder.py 2025-12-09 13:46:12 +00:00
4e1cfb10b4 [develop] Fix check_rabbitmq and check_postgreSQL not starting on Debian 2025-12-05 18:38:08 +03:00
4d767c127b Merge branch release/v9.2.0 into master 2025-12-01 07:23:27 +00:00
71d9e34164 Fix check arm system 2025-11-28 13:54:34 +03:00
d3a2f05945 Merge pull request 'Up version to 9.2.0' (#154) from fix/version9.2.0 into release/v9.2.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/154
2025-11-27 15:13:31 +00:00
97b4421e3c Up version to 9.2.0 2025-11-27 19:50:23 +05:00
73576ff817 Merge branch 'release/v9.2.0' of https://git.onlyoffice.com/ONLYOFFICE/build_tools into release/v9.2.0 2025-11-21 22:48:04 +03:00
b4ba33bb69 Remove AI agent from macos v8 version 2025-11-21 22:47:54 +03:00
e04df1ba9a Optimize package build 2025-11-21 15:43:01 +03:00
dd148a105e Merge pull request 'Fix desktop build' (#152) from fix/sysroot into release/v9.2.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/152
2025-11-14 18:04:41 +00:00
acf75749c2 Fix desktop build 2025-11-14 07:03:45 +03:00
354e4a6a04 Merge pull request 'Fix build with newer qmake' (#151) from fix/sysroot into release/v9.2.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/151
2025-11-13 12:19:21 +00:00
b85fb9c07c Fix build with newer qmake 2025-11-13 14:33:11 +03:00
40b95455a0 Fix package branding build (#146)
Co-authored-by: Semyon Bezrukov <semen.bezrukov@onlyoffice.com>
Co-committed-by: Semyon Bezrukov <semen.bezrukov@onlyoffice.com>
2025-10-28 15:13:29 +00:00
eef1d728b5 Merge branch release/v9.1.0 into develop 2025-10-28 11:57:35 +00:00
cc24ee8e85 Merge branch release/v9.1.0 into master 2025-10-28 11:57:34 +00:00
62ab1f9935 Fix xp build 2025-10-27 18:44:19 +03:00
dd26863a2b Enable agent 2025-10-27 14:25:46 +03:00
c02a387a64 Add support version for v8_89 2025-10-27 14:24:46 +03:00
0fd6852fe8 Merge pull request 'Fix build for mac_64' (#145) from release/v9.1.0 into hotfix/v9.2.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/145
2025-10-27 11:01:07 +00:00
0dbf009c6b Fix build for mac_64 2025-10-27 14:00:06 +03:00
44b6d2d64f Fix windows arm64 packages build 2025-10-23 16:19:18 +03:00
b2ce2bbcad Merge pull request 'release/v9.1.0' (#143) from release/v9.1.0 into hotfix/v9.2.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/143
2025-10-21 20:20:48 +00:00
133192df35 Fix build 2025-10-20 21:14:13 +03:00
c45cf77747 Fix socket.io fetch 2025-10-19 20:41:21 +03:00
9f898d6873 Merge pull request 'feature/win-arm-qemu' (#142) from feature/win-arm-qemu into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/142
2025-10-18 08:48:04 +00:00
61bf2d9413 Fix typo 2025-10-18 11:47:14 +03:00
0c8b3e5919 Refactoring 2025-10-18 11:44:05 +03:00
6fd500e55d Remove unused code 2025-10-17 19:07:17 +03:00
10f7dcee17 Add exe extension 2025-10-17 19:02:45 +03:00
2fcdef2e00 Add README.md 2025-10-17 18:36:20 +03:00
2ec27065f8 Add start.bat and gitignore 2025-10-17 18:18:27 +03:00
4f804ba0e6 Add shutdown 2025-10-17 18:02:16 +03:00
7fb05a51f9 Fix bug 2025-10-17 17:51:27 +03:00
30e3202fd5 Rewrite deploying 2025-10-17 17:40:35 +03:00
b6b09d17f4 Add config option 2025-10-17 14:38:46 +03:00
8a70f3d14a Add generation for all products 2025-10-17 14:03:13 +03:00
aaa5096526 Fix bug 2025-10-16 17:05:44 +03:00
668aa88c6e Developing execution win arm qemu 2025-10-16 17:03:01 +03:00
ec544ca9ab Developing 2025-10-16 14:34:16 +03:00
17ee00a04c Fix windows build 2025-10-15 17:25:47 +03:00
bc4daa9546 Merge pull request '[jsdoc] Fix paths' (#141) from fix/js-doc into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/141
2025-10-15 11:14:48 +00:00
099003b250 [jsdoc] Fix paths 2025-10-15 18:11:05 +07:00
4ca5e4bcb5 Merge branch release/v9.1.0 into master 2025-10-14 14:34:05 +00:00
ee4469885e Merge pull request 'Fix intecative request to keyboard' (#140) from fix/docker-debconfig into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/140
2025-10-13 14:41:47 +00:00
1350955f67 Fix intecative request to keyboard 2025-10-13 18:47:08 +05:00
de36280085 Fix build 2025-10-13 13:17:08 +03:00
0399ea1cb1 Merge pull request 'fix/docker-cmake3.30' (#139) from fix/docker-cmake3.30 into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/139
2025-10-12 14:24:30 +00:00
41ed9824ab Disable ai-agent 2025-10-10 14:23:22 +03:00
842df5ffd0 Up version to 9.1.0 2025-10-08 14:49:36 +05:00
d0e3f36313 Add cmake3.30 in Dockerfile 2025-10-08 12:26:25 +05:00
2378bb91fd Merge branch 'release/v9.1.0' into develop 2025-10-06 12:29:25 +03:00
18605dc68f Merge pull request 'Fix get python in Dockerfile' (#138) from fix/docker into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/138
2025-10-06 08:54:10 +00:00
5004a30908 Fix get python in Dockerfile 2025-10-06 13:48:43 +05:00
33e41ed704 Fix bug 77116 - Fix mac license 2025-10-02 12:30:12 +03:00
1f4c88a489 Merge pull request 'Fix icu deploy' (#134) from fix/icu-deploy-sysroot into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/134
2025-10-02 09:26:29 +00:00
283977f014 Fix icu deploy 2025-10-02 12:22:19 +03:00
e71e0bee74 Merge pull request 'fix/sysroot' (#133) from fix/sysroot into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/133
2025-10-02 05:46:32 +00:00
fe9847f246 Remove unused 2025-10-02 00:14:35 +03:00
f79bfd099b Add libs 2025-10-01 22:11:19 +03:00
3ec9b4dba5 Add libs to sysroot 2025-10-01 21:50:54 +03:00
0c7348fc89 Change b2 addon for boost build 2025-10-01 19:34:20 +03:00
97b615c36a Fix build 2025-10-01 13:03:38 +03:00
59f95f52ce Fix missprint 2025-10-01 10:36:11 +03:00
ba04810793 Fix PATH for python 2025-10-01 10:29:24 +03:00
aea03d8554 FIx v8 build 2025-10-01 07:57:08 +03:00
09247281ef Fix for gcc as util
For 24.04 ubuntu
2025-10-01 06:42:38 +03:00
7b31d85c7c Merge pull request 'Remove libnuma dependency on linux' (#131) from fix/libheif into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/131
2025-09-30 09:22:08 +00:00
3ad5b9d05d Remove libnuma dependency on linux 2025-09-30 13:19:25 +04:00
894f23b292 Merge pull request 'Fix bug 77028' (#130) from fix/bug-77028 into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/130
2025-09-29 07:41:45 +00:00
125d0112e4 Fix typo 2025-09-26 19:48:28 +03:00
b46d6075e4 Fix bug 77028 2025-09-26 19:17:59 +04:00
c535c411da Developing 2025-09-26 16:50:57 +03:00
599db1f8be Developing 2025-09-26 16:22:48 +03:00
10ff21a974 Merge pull request 'Add xcprivacy file generation' (#128) from fix/ios-xcprivacy into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/128
2025-09-26 12:54:35 +00:00
c60dc81694 Add xcprivacy file generation 2025-09-26 16:49:54 +04:00
6de1859441 Revert changes with plist 2025-09-26 15:11:28 +03:00
70a50da160 Add check module version 2025-09-26 14:43:39 +03:00
3ca2efeb30 Merge branch 'release/v9.1.0' of git.onlyoffice.com:ONLYOFFICE/build_tools into release/v9.1.0 2025-09-26 14:33:09 +03:00
496594e77f Change cef package 2025-09-26 14:32:59 +03:00
8ebc7dba86 Fix typo 2025-09-26 12:29:43 +03:00
7da1a18753 Fix typo 2025-09-26 11:53:54 +03:00
c16242f25f Test for sign 2025-09-26 11:32:02 +03:00
2f43d90ab9 Merge pull request 'Fix bug 76997' (#127) from fix/bug-76997 into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/127
2025-09-25 15:15:26 +00:00
6f242eee81 Merge branch 'release/v9.1.0' into fix/bug-76997 2025-09-25 19:14:17 +04:00
f19a406ad2 Fix bug 76997 2025-09-25 19:08:20 +04:00
cfe69c6bcd Merge pull request 'fix/custom-sysroot' (#125) from fix/custom-sysroot into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/125
2025-09-24 07:18:30 +00:00
dcbeec9562 Fix flags for gcc >= 5.4 2025-09-24 09:56:18 +03:00
954e3b1ee8 Fix typo 2025-09-23 18:56:23 +03:00
d5666b1152 Fix typo 2025-09-23 18:35:13 +03:00
c4fa19a1c8 Fix build 2025-09-23 18:11:18 +03:00
7d9444a727 Enable ai agent 2025-09-23 17:56:32 +03:00
3646a2560a Add flag for build qt 2025-09-23 16:45:37 +03:00
2f877e0a4c Disable win_64 platform when build win_arm64 crossversion 2025-09-22 16:48:53 +03:00
434d6cc33c Add deploy ai-agent plugin 2025-09-22 12:13:09 +03:00
31386ad676 Fix xp deploy 2025-09-17 11:20:04 +03:00
0d917471a7 Merge pull request 'Build old icu version for Windows XP' (#126) from fix/icu-win-xp into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/126
2025-09-16 12:36:52 +00:00
4d91ac47ec Build old icu version for win_xp 2025-09-15 19:02:38 +04:00
44e346210b Remove fixed ndk version 2025-09-11 18:45:35 +03:00
e349f3dba2 Merge branch hotfix/v9.0.4 into develop 2025-09-11 08:38:15 +00:00
2ffc1b9347 Merge branch hotfix/v9.0.4 into master 2025-09-11 08:38:14 +00:00
7a58da1af1 Change custom-sysroot to sysroot 2025-09-09 15:36:53 +03:00
f703663342 Fix missprint 2025-09-09 14:00:03 +03:00
af9db63711 Update for sysroot build 2025-09-09 13:45:01 +03:00
7e9d9051aa Fix build 2025-09-09 12:27:25 +03:00
73b41a5588 Merge pull request '[deploy] Deploy admin panel' (#124) from feature/admin-panel into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/124
2025-09-04 23:41:14 +00:00
1ab43f0551 [deploy] Deploy admin panel 2025-09-05 00:37:27 +03:00
79c4fa5042 Fix comment 2025-09-02 21:32:51 +03:00
944caac250 Fix build heif with custom-sysroot 2025-09-02 21:22:43 +03:00
7fe4ade155 Fix missprint 2025-09-02 20:51:29 +03:00
3d51a5e648 Fix build 2025-09-02 19:14:20 +03:00
d6af99141d Fix icu qt deploy 2025-09-02 12:32:38 +03:00
568e84275c Merge pull request 'Fix .dat file deploy' (#123) from fix/android-build into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/123
2025-09-01 08:20:54 +00:00
de5e5fe938 Fix .dat file deploy 2025-09-01 12:18:50 +04:00
152b524197 Merge pull request 'Fix android build' (#122) from fix/android-build into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/122
2025-08-29 13:56:30 +00:00
142e6541c7 Fix android build 2025-08-29 17:32:48 +04:00
357db616f7 Merge pull request 'hotfix/v9.0.4' (#121) from hotfix/v9.0.4 into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/121
2025-08-28 10:58:24 +00:00
8fc50cc0f2 Add support win_arm64 build 2025-08-26 16:11:04 +03:00
1feed69b71 Fix xp build 2025-08-26 10:36:30 +03:00
6b03a77791 Fix linux arm build 2025-08-26 10:15:32 +03:00
31b4e29e37 Merge pull request 'Fix libheif compilation for Linux arm64' (#119) from fix/libheif-linux-arm into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/119
2025-08-25 11:59:21 +00:00
08d8a02166 Simplified ios toolchain finding 2025-08-25 15:36:16 +04:00
e69a5135da Fix for linux_arm64 2025-08-25 15:19:21 +04:00
e768f17744 Add win_arm64 version (libheif) 2025-08-25 13:54:05 +03:00
4e9251e45b Fix build 2025-08-25 13:04:35 +03:00
2ec2173cad Fix build 2025-08-25 00:21:40 +03:00
72d6298bbc Merge pull request 'feature/custom-sysroot' (#118) from feature/custom-sysroot into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/118
2025-08-22 19:51:35 +00:00
a00302ad1d Merge pull request 'Add libheif and Switch to mac framework builds' (#100) from feature/mac-frameworks into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/100
2025-08-22 19:37:57 +00:00
56c93f97af Merge branch 'feature/libheif' into feature/mac-frameworks 2025-08-22 18:22:48 +04:00
a7432a6b4b Merge branch 'release/v9.1.0' into feature/mac-frameworks 2025-08-22 18:21:29 +04:00
75faf391b6 Merge branch 'release/v9.1.0' into feature/custom-sysroot 2025-08-22 14:40:15 +03:00
9cefa13362 Add android builds 2025-08-21 19:19:18 +04:00
8f9835a7bd Major refactoring and code duplication removing 2025-08-20 21:56:10 +04:00
41a4e81c5a Merge pull request 'feature/qt-win-arm64' (#116) from feature/qt-win-arm64 into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/116
2025-08-20 14:55:27 +00:00
83374aa635 Refactoring win_arm64 build 2025-08-20 17:53:19 +03:00
4e6b45043a Update for custom arm toolchain 2025-08-20 17:47:45 +03:00
72124a2305 Disable QtQuick and qml building 2025-08-20 17:07:43 +03:00
b4b84dc462 Add cross building for linux arm64 2025-08-20 14:47:41 +03:00
6cd690d6b6 Disable build online installer 2025-08-20 09:12:53 +00:00
b4f863f00f Add iOS builds 2025-08-20 11:51:49 +04:00
88d0c54040 Add include to deploy 2025-08-19 18:45:14 +03:00
46a18695fa Add fetching qt win arm64 2025-08-19 18:26:13 +03:00
05f2f636df Fix build on intel chip 2025-08-19 18:07:18 +03:00
506f558eed Fix deploy 2025-08-19 15:50:55 +03:00
38cd4e6e77 Merge pull request 'release/v9.1.0' (#115) from release/v9.1.0 into feature/qt-win-arm64 2025-08-19 12:30:28 +00:00
f86376fbc2 Fix deploy for desktop 2025-08-19 15:29:26 +03:00
3943237a64 Fix build for desktop win arm64 2025-08-19 14:39:55 +03:00
37d256acda Remove unused project 2025-08-19 12:58:24 +03:00
9cad51e611 All in one folder for easy cross-building 2025-08-19 11:40:39 +03:00
3571e02abf Delete non-used code 2025-08-19 10:48:49 +03:00
123af8265a Fix typo 2025-08-19 10:46:11 +03:00
4f55f8cc90 Merge branch 'release/v9.1.0' of git.onlyoffice.com:ONLYOFFICE/build_tools into release/v9.1.0 2025-08-19 09:50:56 +03:00
9a3642572c set -isysroot to x86_64 build 2025-08-19 09:50:48 +03:00
b93773ec78 Module version Up (for mac) 2025-08-19 09:28:52 +03:00
d2eb9b20af Merge branch 'release/v9.1.0' of git.onlyoffice.com:ONLYOFFICE/build_tools into release/v9.1.0 2025-08-19 09:27:23 +03:00
f2cb4e6a51 Fix icu build 2025-08-19 09:27:05 +03:00
5cdb4dae39 Fix typo 2025-08-19 08:36:44 +03:00
9102284b80 Fix mac icu version 2025-08-19 07:54:42 +03:00
1f009d0942 Up version module 2025-08-19 07:51:11 +03:00
566367daa5 Fix typo 2025-08-19 07:43:21 +03:00
c36a390292 Fix cross-compilation 2025-08-19 02:43:16 +03:00
4a947d2a3c Fix build 2025-08-18 23:43:40 +03:00
9b104f358e Revert use git for sources (save executable rights) 2025-08-18 23:03:32 +03:00
c8a72c53b8 Start developing build qt for win arm64 2025-08-18 19:55:05 +03:00
6a3d29bd00 Merge pull request 'feature/win-arm64' (#114) from feature/win-arm64 into release/v9.1.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/114
2025-08-16 11:15:11 +00:00
97c8a33e5d Fix macOS desktop license 2025-08-15 18:26:48 +03:00
39ff7e7692 Fix for builder deploy 2025-08-15 14:49:45 +03:00
4f9040d73b Fix typo 2025-08-15 12:52:33 +03:00
9579394788 Fix deploy for win_arm64 2025-08-15 12:20:53 +03:00
ea8a7bf52e Add macOS desktop commercial build 2025-08-15 12:11:51 +03:00
a1af0a6804 Patch for vs2019 2025-08-15 10:47:03 +03:00
65e6f05385 Build win_64 if win_arm64 2025-08-15 09:49:52 +03:00
08145c0d79 Fix typo 2025-08-15 09:27:56 +03:00
c1f60e27c8 Force update icu version 2025-08-15 09:27:44 +03:00
5168e20918 Fix bug for default build 2025-08-14 23:29:38 +03:00
0968ca2566 [desktop] fix build for macOS 2025-08-14 23:21:21 +03:00
e461da3a1e [desktop] copy license for macOS bundle 2025-08-14 21:20:12 +03:00
c6176a95f4 Fix icu build 2025-08-14 17:02:36 +03:00
224db5eb54 Merge pull request 'Fix bug 75808' (#110) from fix/bug-75808 into release/v9.1.0 2025-08-14 13:15:57 +00:00
a6c315302b [win] fix bug 75808 2025-08-14 15:59:03 +03:00
ae3bc78b29 Fix build for win 11 2025-08-14 13:16:16 +03:00
4c48514402 Fix boost build 2025-08-14 01:27:57 +03:00
e4bc6492be Change spec 2025-08-13 21:21:37 +03:00
23a526f3a1 Develop 2025-08-13 21:10:58 +03:00
df87cafbdb Developing build for win_arm64 2025-08-13 20:02:49 +03:00
595e2eaa86 Develop build for win_arm64 2025-08-13 18:35:12 +03:00
559837ee13 Merge pull request 'fix/js-doc-dev' (#109) from fix/js-doc-dev into develop 2025-08-13 15:29:58 +00:00
9440fc3494 Merge pull request 'fix/js-doc' (#108) from fix/js-doc into release/v9.1.0 2025-08-13 15:29:32 +00:00
e700a98963 [jsdoc] Fix typo 2025-08-13 22:26:33 +07:00
9635b8c08f [jsdoc] gen plugins events summary file 2025-08-13 22:26:32 +07:00
56e2042711 [jsdoc] Fix typo 2025-08-13 22:23:21 +07:00
30167dbf76 [jsdoc] gen plugins events summary file 2025-08-13 22:23:16 +07:00
5a403cb3ac Merge pull request 'fix/jsdoc' (#107) from fix/jsdoc into master 2025-08-13 14:29:55 +00:00
f22bda85e4 Fix for old GCC on Linux 2025-08-13 16:30:00 +04:00
4f09833650 Fix win builds
and refactoring
2025-08-12 20:55:47 +04:00
d435dd496b [jsdoc] Fix typo 2025-08-12 19:05:12 +07:00
252a5c306d [jsdoc] gen plugins events summary file 2025-08-12 19:04:28 +07:00
9710a074f8 Add min osx target for mac 2025-08-11 18:33:42 +04:00
e4b91f1b24 Fix desktop build 2025-08-11 15:10:26 +03:00
6b3f100e7e Finish mac builds and major refactoring 2025-08-09 00:19:17 +04:00
573612c1c5 Fix desktop commercial windows package name 2025-08-08 20:05:45 +03:00
ba31642a46 Add x265 build for mac 2025-08-07 20:05:08 +04:00
00c37bc9dd Merge branch 'feature/mac-frameworks' into feature/libheif 2025-08-07 16:56:57 +04:00
878057972b Merge pull request 'Remove online installer' (#104) from feature/remove-online-installer into develop 2025-08-07 08:19:24 +00:00
1f7a7fc452 [win] remove online installer 2025-08-06 18:01:14 +03:00
a8ab257faf Merge branch hotfix/v9.0.4 into develop 2025-08-06 08:26:47 +00:00
fc9d060a78 Merge branch hotfix/v9.0.4 into master 2025-08-06 08:26:46 +00:00
a640943c60 Remove 3DPARTYLICENSE from desktop deploy 2025-08-05 18:06:08 +03:00
6c700fe492 Merge branch hotfix/v9.0.4 into master 2025-08-05 14:06:58 +00:00
afb9b8b61a Merge pull request 'Fix bug 76209' (#102) from fix/bug-76209 into hotfix/v9.0.4
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/102
2025-08-05 06:52:50 +00:00
38c49f70e5 Merge pull request 'Up version to 9.0.4' (#101) from fix/version-9.0.4 into hotfix/v9.0.4
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/101
2025-08-05 06:52:16 +00:00
bdc40e1572 Fix bug 76209 2025-08-05 11:44:32 +05:00
73bf068cb0 Up version to 9.0.4 2025-08-04 16:19:16 +05:00
6a4d3bff53 Rename brotli module 2025-08-04 12:32:19 +03:00
dc3ceb6ef8 Change default plugins list 2025-08-03 21:29:06 +03:00
a0cc2123bf Update make.py 2025-07-25 15:08:51 +00:00
c71e5c5bde Update make.py 2025-07-25 06:47:39 +00:00
1f84a604d2 Update make.py 2025-07-24 20:14:14 +00:00
648dc2119e Add fonts to builder 2025-07-24 15:13:06 +03:00
4f746cb484 Fix desktop windows packages build 2025-07-24 12:40:01 +03:00
853c760cc4 Update make.py 2025-07-24 06:25:19 +00:00
972bcc8064 Add build_type 2025-07-24 02:10:03 +03:00
2b6557f0ec [desktop] set test license type 2025-07-24 00:57:58 +03:00
053c0c2fe9 [desktop] revert test changes 2025-07-23 20:00:53 +00:00
caf79933d8 [desktop] test license's type 2025-07-23 20:45:04 +03:00
debe284664 Add Windows & Linux DesktopEditors Commercial build 2025-07-23 12:39:24 +03:00
d4231e0efa Correct Info.plist files for mac frameworks 2025-07-21 17:59:40 +04:00
de99e3f62e Remove ICU libraries for core and server builds 2025-07-21 16:41:50 +04:00
16858aa7c2 Add new module 2025-07-20 14:51:56 +03:00
8beb8b3c84 Change deploy for desktop 2025-07-18 12:04:33 +04:00
8cf076aff8 Change deploy for builder 2025-07-18 12:00:19 +04:00
55ddce5904 Add static build for ICU libraries 2025-07-18 12:00:19 +04:00
aa5d06a1ec Fix static build 2025-07-17 20:12:31 +03:00
031a1119d6 Fix deploy 2025-07-17 18:17:42 +03:00
316c3cec26 fix conflict file 2025-07-17 09:16:17 +03:00
584513fb15 Update url for glew 2025-07-15 16:01:59 +03:00
9203d68ed8 Add remark in configure.py 2025-07-15 15:52:43 +03:00
41abb6b09c Update for non-linux platforms 2025-07-15 15:31:03 +03:00
7bb5e65810 Update configuration 2025-07-15 14:52:12 +03:00
49ede6a10b Add removing tar after unpacking 2025-07-15 14:42:34 +03:00
ac394d8de6 Update for github downloading 2025-07-15 14:20:42 +03:00
abde837a74 Update arm toolchain to gcc 5.4.1 2025-07-14 20:47:06 +03:00
834fab5fc7 Update .github/workflows/git-operations.yml 2025-07-10 06:11:12 +00:00
d357abcfc9 Update .github/workflows/git-operations.yml 2025-07-10 06:09:09 +00:00
119b5f6d33 Merge pull request 'feature/git-operations' (#95) from feature/git-operations into master 2025-07-09 08:53:03 +00:00
8a70714eeb add gh action 2025-07-09 11:52:03 +03:00
90903009f4 add git operations 2025-07-09 11:43:49 +03:00
6f256be099 add get base url for git 2025-07-09 11:43:39 +03:00
5568b7da2e Fix errors and bugs 2025-07-08 23:16:28 +03:00
0f89ba4247 Restore LD_LIBRARY_PATH after building
And fix bugs
2025-07-08 21:02:32 +03:00
4cefdc38fb FIx symlinks for entire folder 2025-07-08 19:53:46 +03:00
d1481021a7 Add custom sysroot for arm 2025-07-08 18:34:40 +03:00
d7eaef6503 Merge branch 'hotfix/v9.0.3'
# Conflicts:
#	scripts/sdkjs_common/jsdoc/config/plugins/cell.json
#	scripts/sdkjs_common/jsdoc/config/plugins/common.json
#	scripts/sdkjs_common/jsdoc/config/plugins/correct_doclets.js
#	scripts/sdkjs_common/jsdoc/config/plugins/events/correct_doclets.js
#	scripts/sdkjs_common/jsdoc/config/plugins/forms.json
#	scripts/sdkjs_common/jsdoc/config/plugins/methods/cell.json
#	scripts/sdkjs_common/jsdoc/config/plugins/methods/common.json
#	scripts/sdkjs_common/jsdoc/config/plugins/methods/forms.json
#	scripts/sdkjs_common/jsdoc/config/plugins/methods/slide.json
#	scripts/sdkjs_common/jsdoc/config/plugins/methods/word.json
#	scripts/sdkjs_common/jsdoc/config/plugins/slide.json
#	scripts/sdkjs_common/jsdoc/config/plugins/word.json
#	scripts/sdkjs_common/jsdoc/office-api/config/cell.json
#	scripts/sdkjs_common/jsdoc/office-api/config/forms.json
#	scripts/sdkjs_common/jsdoc/office-api/config/pdf.json
#	scripts/sdkjs_common/jsdoc/office-api/config/slide.json
#	scripts/sdkjs_common/jsdoc/office-api/config/word.json
#	scripts/sdkjs_common/jsdoc/office-api/generate_docs_md.py
#	scripts/sdkjs_common/jsdoc/plugins/config/correct_doclets.js
#	scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_json.py
#	version
2025-07-08 17:59:48 +03:00
0c7b8a2b1c Merge pull request '[jsdoc] Fix replace <br> tag' (#93) from fix/js-doc into hotfix/v9.0.3
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/93
2025-07-08 08:57:03 +00:00
9b5b3eb77c [jsdoc] Fix replace <br> tag 2025-07-08 15:51:43 +07:00
9e31770bfa Merge pull request 'fix/js-doc' (#92) from fix/js-doc into hotfix/v9.0.3
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/92
2025-07-08 08:17:40 +00:00
4d6b9f9463 Fix md 2025-07-08 15:15:55 +07:00
d2c79bb78d [jsdoc] Refactoring 2025-07-08 15:15:49 +07:00
e0aa6184d6 Remove main 2025-07-07 18:06:50 +03:00
9c80b95dbe Add build for linux and mac 2025-07-07 18:00:44 +03:00
bfd3bb009f Merge branch hotfix/v9.0.2 into develop 2025-07-07 14:37:18 +00:00
7ef302fac1 Merge branch hotfix/v9.0.2 into master 2025-07-07 14:37:18 +00:00
fece05de0b Merge pull request 'Up version to 9.0.3' (#90) from fix/version9.0.3 into hotfix/v9.0.3
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/90
2025-07-07 08:44:32 +00:00
71a2981ae8 Up version to 9.0.3 2025-07-07 13:12:43 +05:00
de1d437576 Remove temporary logs. Add verbose to install modules if failed 2025-07-03 13:10:37 +03:00
2e179644b3 Add verbose for debug npm/grunt 2025-07-03 11:50:44 +03:00
c4551af253 Add temporary logs 2025-07-03 11:07:08 +03:00
28ca6676a5 Fix libheif build 2025-07-01 13:11:35 +03:00
31f679a050 [js] added some logs 2025-06-30 17:10:30 +03:00
64c32043cc Fix cmake build 2025-06-30 16:07:37 +03:00
ea52e70a6d Merge pull request '[macros][libs] Remove extra methods info' (#88) from fix/macros-libs into hotfix/v9.0.3
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/88
2025-06-30 11:34:32 +00:00
1d721e3e3e [macros][libs] Remove extra methods info 2025-06-30 18:33:47 +07:00
3e3b0127a6 Merge pull request '[macros][libs] Added forms to generation' (#87) from fix/macros-libs into hotfix/v9.0.3
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/87
2025-06-30 11:02:31 +00:00
dcc9f8e669 [macros][libs] Added forms to generation 2025-06-30 17:58:52 +07:00
67c454b469 Fix build 2025-06-27 22:47:45 +03:00
1cc0528b11 Fix build 2025-06-27 18:25:09 +03:00
c3dce4bc91 Merge branch release/v9.0.0 into develop 2025-06-27 14:17:01 +00:00
990382512b Merge branch release/v9.0.0 into master 2025-06-27 14:17:01 +00:00
b6985ce27e Merge pull request '[jsdoc] Generating plugin events docs' (#86) from feature/jsdoc-plugin-events into hotfix/v9.0.2
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/86
2025-06-27 08:39:47 +00:00
65e36cd01a [jsdoc] Generating plugin events docs 2025-06-27 15:35:17 +07:00
caaebde240 [develop] Add note to clarify that ARM is unsupported 2025-06-27 10:37:36 +03:00
7c130faac2 Merge branch hotfix/v9.0.2 into master 2025-06-26 12:53:46 +00:00
7ff5d2f40d Merge pull request 'Up version to 9.0.2' (#85) from fix/version9.0.2 into hotfix/v9.0.2
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/85
2025-06-26 07:46:05 +00:00
a353e89871 Up version to 9.0.2 2025-06-26 12:40:26 +05:00
c9151cd09d Add custom sysroot support 2025-06-24 22:28:32 +03:00
03f99c526d Merge pull request 'Add built-in Date to primitive types' (#84) from fix/generation-data-type into release/v9.0.0 2025-06-19 13:49:23 +00:00
34a54bf88f Add built-in Date to primitive types 2025-06-19 16:42:20 +03:00
b40c0a0d74 Merge branch release/v9.0.0 into master 2025-06-18 09:22:00 +00:00
519ea3fb6c Fix libde265 build 2025-06-17 01:30:19 +03:00
e4cc090bfd Merge pull request '[build] Bump Ubuntu to 20.04(Dockerfile); switch to @yao-pkg/pkg; For bug 74773' (#83) from fix/bug-74773-2 into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/83
2025-06-11 12:56:35 +00:00
c6138b3902 Merge pull request 'Up version to 9.0.0' (#82) from fix/version9.0.0 into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/82
2025-06-10 07:22:28 +00:00
8ec240dcee Up version to 9.0.0 2025-06-10 11:34:31 +05:00
fa589c9523 [build] Bump Ubuntu to 20.04(Dockerfile); switch to @yao-pkg/pkg; For bug 74773 2025-06-06 17:45:07 +03:00
5f2d8be5dc Add libheif to 3dParty 2025-06-06 16:55:06 +03:00
829228d28c Fix builder deploy s3 paths 2025-06-05 16:49:13 +03:00
a7c9f3a0ce Add builder python wheel package build 2025-06-05 12:09:40 +03:00
e676ebcffd Enable minimization for native editors 2025-05-29 23:12:26 +03:00
6fe22b14c6 Add script for update dictionaries 2025-05-29 22:34:42 +03:00
d50b171b54 Optimize packages build 2025-05-28 13:24:18 +03:00
d6cbfcbfe3 [build] Bump nodejs version to 20 for all environments 2025-05-15 11:46:05 +03:00
e40f8c9a7e [build] Downgrade to nodejs packaging version to 20 2025-05-10 16:13:42 +03:00
6a2db3d59e [build] Bump nodejs version to 22(linux only) 2025-05-08 01:21:26 +03:00
48c8a635a8 [build] Bump nodejs version to 22 2025-05-08 01:11:23 +03:00
1ac83e0ffd Merge pull request '[jsdoc] Escape [] in quotes' (#78) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/78
2025-05-07 08:42:37 +00:00
ef6ecbbebd [jsdoc] Escape [] in quotes 2025-05-07 15:38:56 +07:00
f51b841320 Merge pull request 'Added md fetch' (#77) from feature/md2html into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/77
2025-05-05 19:09:39 +00:00
7c0099c57e Added md fetch 2025-05-05 15:21:00 +03:00
8f49dce1ed Merge pull request 'refactor: remove semicolon from python scripts' (#76) from pysemicolon into release/v9.0.0 2025-05-01 05:33:19 +00:00
a9bad0d5b7 Fix check arm system 2025-04-23 14:35:34 +03:00
702a83c010 Merge pull request 'Added OFD format' (#73) from feature/ofd into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/73
2025-04-23 09:31:37 +00:00
ee52dbe5c4 [deploy] Add check is_exist for addon path 2025-04-22 15:41:28 +03:00
0f0e0a0e52 [deploy] Build and deploy server grunt module 2025-04-22 10:40:30 +03:00
b87e305c06 Merge branch hotfix/v8.3.3 into release/v9.0.0 2025-04-21 08:57:49 +00:00
8516b163b4 Added OFD format 2025-04-19 17:35:55 +03:00
4460d6ed13 Merge branch hotfix/v8.3.3 into master 2025-04-16 07:30:59 +00:00
ab3165fdaf Add script for minimization js 2025-04-14 20:57:52 +03:00
2ab7616132 Fix typo 2025-04-07 12:25:42 +03:00
3be471b472 Merge pull request 'feature/clang' (#71) from feature/clang into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/71
2025-04-06 09:55:15 +00:00
a151375339 Remove spaces 2025-04-06 12:54:23 +03:00
ed2ab2d80b Refactoring 2025-04-06 12:53:03 +03:00
35f99ac3a0 Fix icu build 2025-04-04 10:20:24 +03:00
836a0401ed Testing clang build 2025-04-04 08:55:38 +03:00
b8024df7d3 Merge branch hotfix/v8.3.2 into release/v9.0.0 2025-04-01 08:32:43 +00:00
7ee66bbafd Merge branch hotfix/v8.3.2 into master 2025-04-01 08:32:40 +00:00
c6acd6cdcd Merge pull request '[jsdoc] Fix write inherited methods' (#69) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/69
2025-03-27 11:42:25 +00:00
cee122afa5 [jsdoc] Fix write inherited methods 2025-03-27 09:53:10 +07:00
bf7df0b45a Merge pull request 'hotfix/v8.3.2' (#67) from hotfix/v8.3.2 into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/67
2025-03-21 11:43:22 +00:00
50c312513c Merge pull request '[jsdoc] Fix dataset system message' (#65) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/65
2025-03-18 10:01:27 +00:00
427ae97dd2 [jsdoc] Fix dataset system message 2025-03-18 16:59:58 +07:00
d80f1f1b0f [develop] Parse config to fix install_mysqlserver 2025-03-18 10:19:18 +03:00
3f4d0cefa8 Merge pull request 'hotfix/v8.3.2' (#63) from hotfix/v8.3.2 into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/63
2025-03-12 07:58:39 +00:00
8dcf0277ac Merge pull request '[jsdoc] Fix typo' (#62) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/62
2025-03-10 07:49:10 +00:00
6664051127 [jsdoc] Fix typo 2025-03-10 14:47:55 +07:00
019f10ee86 Merge pull request '[jsdoc] Fix expression example' (#61) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/61
2025-03-07 14:59:01 +00:00
a7ee5d5679 [jsdoc] Generation for site 2025-03-07 21:36:25 +07:00
7c31890fc0 [jsdoc] Fix expression example 2025-03-07 16:41:06 +07:00
ad9258710b Merge pull request '[jsdoc] Fix prev' (#60) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/60
2025-03-07 09:21:06 +00:00
626dd37312 [jsdoc] Fix prev 2025-03-07 16:07:23 +07:00
b4d95ccbb9 Merge pull request '[jsdoc] Rename md docs folders' (#58) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/58
2025-03-07 08:54:22 +00:00
0d0ae2b5e6 [jsdoc] Rename md docs folders 2025-03-07 15:49:57 +07:00
d7b3b7f120 Merge pull request '[jsdoc] Write only used enums' (#57) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/57
2025-03-07 08:20:20 +00:00
7a864171b3 [jsdoc] Write only used enums 2025-03-07 15:04:48 +07:00
8cfb8f3d84 Merge pull request '[jsdoc] Fix path' (#56) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/56
2025-03-06 13:58:01 +00:00
2f39454d31 [jsdoc] Fix path 2025-03-06 20:51:42 +07:00
d3d1080c89 Merge pull request '[jsdoc] Fix bug, sort props/methods' (#55) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/55
2025-03-06 11:35:59 +00:00
539597f07c [jsdoc] Fix bug, sort props/methods, handle @link tag 2025-03-06 18:30:24 +07:00
40b11e192d Merge pull request '[jsdoc] Removed editor tag in plugins examples' (#54) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/54
2025-03-06 06:45:33 +00:00
72cc19f346 [jsdoc] Removed editor tag in plugins examples 2025-03-06 13:43:56 +07:00
efb22f741f Merge pull request '[jsdoc] Fixes docs generation' (#53) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/53
2025-03-05 12:07:32 +00:00
2458673d3c [jsdoc] Fixes docs generation 2025-03-05 19:01:28 +07:00
3881a6659e Merge pull request '[jsdoc] Class markdown, data types fixes' (#52) from fix/js-doc into release/v9.0.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/52
2025-03-04 12:59:06 +00:00
a567cc2222 [jsdoc] Class markdown, data types fixes 2025-03-04 19:43:48 +07:00
adc353cdcc refactor: remove semicolon from python scripts
Semicolons are unnecessary when there's only one statement on a single line.
2024-09-29 21:46:41 +08:00
111 changed files with 6219 additions and 1907 deletions

88
.github/workflows/git-operations.yml vendored Normal file
View File

@ -0,0 +1,88 @@
name: Git Operations
on:
workflow_dispatch:
inputs:
operation:
description: 'Operation to perform'
required: true
type: choice
options:
- create
- remove
default: 'create'
branch_name:
description: 'Branch name to create or remove'
required: true
type: string
base_branch:
description: 'Base branch to work from (for create operation)'
required: false
type: string
default: 'develop'
branding:
description: 'Branding name'
required: false
type: string
default: 'onlyoffice'
branding_url:
description: 'Branding repository URL (relative to git host)'
required: false
type: string
default: 'ONLYOFFICE/onlyoffice.git'
jobs:
git-operations:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
path: ONLYOFFICE/build_tools
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# Install any Python dependencies if requirements.txt exists
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Configure Git
run: |
git config --global user.name "GitHub Actions Bot"
git config --global user.email "actions@github.com"
- name: Run Git Operations
run: |
cd ONLYOFFICE/build_tools/scripts/develop
python git_operations.py ${{ inputs.operation }} "${{ inputs.branch_name }}" \
--base-branch="${{ inputs.base_branch }}" \
--branding="${{ inputs.branding }}" \
--branding-url="${{ inputs.branding_url }}" \
--modules="${{ inputs.modules }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Operation Summary
run: |
echo "## Git Operations Summary" >> $GITHUB_STEP_SUMMARY
echo "- **Operation**: ${{ inputs.operation }}" >> $GITHUB_STEP_SUMMARY
echo "- **Branch Name**: ${{ inputs.branch_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **Base Branch**: ${{ inputs.base_branch }}" >> $GITHUB_STEP_SUMMARY
echo "- **Branding**: ${{ inputs.branding }}" >> $GITHUB_STEP_SUMMARY
echo "- **Branding URL**: ${{ inputs.branding_url }}" >> $GITHUB_STEP_SUMMARY
echo "- **Modules**: ${{ inputs.modules }}" >> $GITHUB_STEP_SUMMARY
if [ "${{ inputs.operation }}" = "remove" ] && [ "${{ inputs.force_remove }}" = "true" ]; then
echo "- **Force Remove**: Yes" >> $GITHUB_STEP_SUMMARY
fi

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ config
*.*~
**~
*.DS_Store
.idea
scripts/license_checker/reports
tests/puppeteer/node_modules
tests/puppeteer/work_directory

View File

@ -1,14 +1,45 @@
FROM ubuntu:16.04
FROM ubuntu:24.04
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN echo 'keyboard-configuration keyboard-configuration/layoutcode string us' | debconf-set-selections && \
echo 'keyboard-configuration keyboard-configuration/modelcode string pc105' | debconf-set-selections
RUN apt-get -y update && \
apt-get -y install python \
python3 \
sudo
RUN rm /usr/bin/python && ln -s /usr/bin/python2 /usr/bin/python
apt-get -y install sudo \
git \
git-lfs \
curl \
wget \
p7zip-full
ADD . /build_tools
WORKDIR /build_tools
CMD ["sh", "-c", "cd tools/linux && python3 ./automate.py"]
# Install local Python
RUN cd tools/linux && \
./python.sh
# Fetch Qt binaries
RUN cd tools/linux && \
./python3/bin/python3 ./qt_binary_fetch.py amd64
# Install system dependencies
RUN cd tools/linux && \
./python3/bin/python3 ./deps.py
# Install CMake
RUN cd tools/linux && \
./cmake.sh
# Fetch sysroot
RUN cd tools/linux/sysroot && \
../python3/bin/python3 ./fetch.py amd64
ARG BRANCH=master
ENV BRANCH=${BRANCH}
CMD ["sh", "-c", "./tools/linux/python3/bin/python3 ./configure.py --sysroot \"1\" --clean \"0\" --update-light \"1\" --branch \"${BRANCH}\" --update \"1\" --module \"desktop server builder\" --qt-dir \"$(pwd)/tools/linux/qt_build/Qt-5.9.9\" && ./tools/linux/python3/bin/python3 ./make.py"]

380
README.md
View File

@ -1,221 +1,218 @@
# build_tools
<h1>ONLYOFFICE Build Tools</h1>
## Overview
Welcome to the ```build_tools``` repository! This powerful toolkit simplifies the process of compiling [ONLYOFFICE](https://github.com/ONLYOFFICE) products from source on Linux.
**build_tools** allow you to automatically get and install all the components
necessary for the compilation process, all the dependencies required for the
**ONLYOFFICE Document Server**, **Document Builder** and **Desktop Editors**
correct work, as well as to get the latest version of
**ONLYOFFICE products** source code and build all their components.
It automatically fetches all the required dependencies and source code to build the latest versions of:
**Important!** We can only guarantee the correct work of the products built from
the `master` branch.
* [Docs (Document Server)](https://www.onlyoffice.com/docs?utm_source=github&utm_medium=cpc&utm_campaign=GitHubBuildTools)
* [Desktop Editors](https://www.onlyoffice.com/desktop?utm_source=github&utm_medium=cpc&utm_campaign=GitHubBuildTools)
* [Document Builder](https://www.onlyoffice.com/document-builder?utm_source=github&utm_medium=cpc&utm_campaign=GitHubBuildTools)
## How to use - Linux
**A quick note:** For the most stable and reliable builds, we strongly recommend compiling from the ```master``` branch of this repository.
**Note**: The solution has been tested on **Ubuntu 16.04**.
## **How do I use it on Linux? 🐧**
### Installing dependencies
>This guide has been tested and verified on **Ubuntu 16.04**.
You might need to install **Python**, depending on your version of Ubuntu:
### **Step 1: Install dependencies**
First, let's make sure you have **Python** installed, as it's needed to run the build scripts.
```bash
sudo apt-get install -y python
```
### Building ONLYOFFICE products source code
### **Step 2: Build the source code**
1. Clone the build_tools repository:
Now, you're ready to build the ONLYOFFICE products.
```bash
git clone https://github.com/ONLYOFFICE/build_tools.git
1. **Clone the build_tools repository:**
This command downloads the build tools to your machine using Git:
```bash
git clone https://github.com/ONLYOFFICE/build_tools.git
```
2. **Navigate to the scripts directory:**
```bash
cd build_tools/tools/linux
```
3. **Run the automation script:**
This is where the magic happens! Running the script without any options will build all three products: Document Server, Document Builder, and Desktop Editors.
```bash
./automate.py
```
You can also build ONLYOFFICE products separately. Just run the script with the parameter corresponding to the necessary product. For example, to build *Desktop Editors* and *Document Server*
```bash
./automate.py desktop server
```
**Perfect!** Once the script finishes, you will find the compiled products in the ```./out``` directory.
## **Advanced options & different workflows 🚀**
### **How to use Docker**
If you prefer using Docker, you can build all products inside a container. This is a great way to keep your local system clean.
1. **Create an output directory:**
```bash
mkdir out
```
2. Go to the `build_tools/tools/linux` directory:
2. **Build the Docker image:**
```bash
cd build_tools/tools/linux
```bash
docker build --tag onlyoffice-document-editors-builder .
```
3. **Run the container to start the build:**
This command mounts your local out directory into the container, so the final build files will appear on your machine.
```bash
docker run -v $PWD/out:/build_tools/out onlyoffice-document-editors-builder
```
3. Run the `automate.py` script:
You've done it! The results will be in the ```./out``` directory you created.
```bash
./automate.py
```
## **How to build and run the products separately ▶️**
If you run the script without any parameters this allows to build **ONLYOFFICE
Document Server**, **Document Builder** and **Desktop Editors**.
Don't need everything? You can save time by building only the products you need. Just add the product name as an argument to the script.
The result will be available in the `./out` directory.
### Need just the [Document Builder](https://github.com/ONLYOFFICE/DocumentBuilder)❓
* How to build
To build **ONLYOFFICE** products separately run the script with the parameter
corresponding to the necessary product.
```bash
./automate.py builder
```
* How to run
```bash
cd ../../out/linux_64/onlyoffice/documentbuilder
./docbuilder
```
Its also possible to build several products at once as shown in the example
below.
### Need just the [Desktop Editors](https://github.com/ONLYOFFICE/DesktopEditors)❓
**Example**: Building **Desktop Editors** and **Document Server**
* How to build
```bash
./automate.py desktop
```
* How to run
```bash
cd ../../out/linux_64/onlyoffice/desktopeditors
LD_LIBRARY_PATH=./ ./DesktopEditors
```
### Need just the [Docs (Document Server)](https://github.com/ONLYOFFICE/DocumentServer)❓
* How to build
```bash
./automate.py server
```
* How to run
Running the Document Server is a multi-step process because it relies on a few background services. Let's break it down step by step.
#### **Step 1. Set up dependencies**
The Document Server needs a few things to run correctly:
* **NGINX**: Acts as a web server to handle requests.
* **PostgreSQL**: Used as the database to store information.
* **RabbitMQ**: A message broker that helps different parts of the server communicate.
Here are the commands to install and configure them.
#### **Install and configure NGINX**
1. Install NGINX
```bash
sudo apt-get install nginx
```
2. Disable the default NGINX site
```bash
sudo rm -f /etc/nginx/sites-enabled/default
```
3. Set up the new website. To do that create the ```/etc/nginx/sites-available/onlyoffice-documentserver``` file with the following contents:
```bash
./automate.py desktop server
map $http_host $this_host {
"" $host;
default $http_host;
}
map $http_x_forwarded_proto $the_scheme {
default $http_x_forwarded_proto;
"" $scheme;
}
map $http_x_forwarded_host $the_host {
default $http_x_forwarded_host;
"" $this_host;
}
map $http_upgrade $proxy_connection {
default upgrade;
"" close;
}
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Forwarded-Host $the_host;
proxy_set_header X-Forwarded-Proto $the_scheme;
server {
listen 0.0.0.0:80;
listen [::]:80 default_server;
server_tokens off;
rewrite ^\/OfficeWeb(\/apps\/.*)$ /web-apps$1 redirect;
location / {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
}
}
```
### Using Docker
You can also build all **ONLYOFFICE products** at once using Docker.
Build the `onlyoffice-document-editors-builder` Docker image using the
provided `Dockerfile` and run the corresponding Docker container.
4. Enable the new site by creating a symbolic link
```bash
mkdir out
docker build --tag onlyoffice-document-editors-builder .
docker run -v $PWD/out:/build_tools/out onlyoffice-document-editors-builder
sudo ln -s /etc/nginx/sites-available/onlyoffice-documentserver /etc/nginx/sites-enabled/onlyoffice-documentserver
```
The result will be available in the `./out` directory.
### Building and running ONLYOFFICE products separately
#### Document Builder
##### Building Document Builder
5. Restart NGINX to apply the changes
```bash
./automate.py builder
sudo nginx -s reload
```
#### **Install and configure PostgreSQL**
##### Running Document Builder
```bash
cd ../../out/linux_64/onlyoffice/documentbuilder
./docbuilder
```
#### Desktop Editors
##### Building Desktop Editors
```bash
./automate.py desktop
```
##### Running Desktop Editors
```bash
cd ../../out/linux_64/onlyoffice/desktopeditors
LD_LIBRARY_PATH=./ ./DesktopEditors
```
#### Document Server
##### Building Document Server
```bash
./automate.py server
```
##### Installing and configuring Document Server dependencies
**Document Server** uses **NGINX** as a web server and **PostgreSQL** as a database.
**RabbitMQ** is also required for **Document Server** to work correctly.
###### Installing and configuring NGINX
1. Install NGINX:
```bash
sudo apt-get install nginx
```
2. Disable the default website:
```bash
sudo rm -f /etc/nginx/sites-enabled/default
```
3. Set up the new website. To do that create the `/etc/nginx/sites-available/onlyoffice-documentserver`
file with the following contents:
```bash
map $http_host $this_host {
"" $host;
default $http_host;
}
map $http_x_forwarded_proto $the_scheme {
default $http_x_forwarded_proto;
"" $scheme;
}
map $http_x_forwarded_host $the_host {
default $http_x_forwarded_host;
"" $this_host;
}
map $http_upgrade $proxy_connection {
default upgrade;
"" close;
}
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Forwarded-Host $the_host;
proxy_set_header X-Forwarded-Proto $the_scheme;
server {
listen 0.0.0.0:80;
listen [::]:80 default_server;
server_tokens off;
rewrite ^\/OfficeWeb(\/apps\/.*)$ /web-apps$1 redirect;
location / {
proxy_pass http://localhost:8000;
proxy_http_version 1.1;
}
}
```
4. Add the symlink to the newly created website to the
`/etc/nginx/sites-available` directory:
```bash
sudo ln -s /etc/nginx/sites-available/onlyoffice-documentserver /etc/nginx/sites-enabled/onlyoffice-documentserver
```
5. Restart NGINX to apply the changes:
```bash
sudo nginx -s reload
```
###### Installing and configuring PostgreSQL
1. Install PostgreSQL:
1. Install PostgreSQL
```bash
sudo apt-get install postgresql
```
2. Create the PostgreSQL database and user:
**Note**: The created database must have **onlyoffice** both for user and password.
2. Create a database and user.
**Note**: The user and password must both be **'onlyoffice'.**
```bash
sudo -i -u postgres psql -c "CREATE USER onlyoffice WITH PASSWORD 'onlyoffice';"
sudo -i -u postgres psql -c "CREATE DATABASE onlyoffice OWNER onlyoffice;"
```
3. Configure the database:
3. Configure the database:
```bash
psql -hlocalhost -Uonlyoffice -d onlyoffice -f ../../out/linux_64/onlyoffice/documentserver/server/schema/postgresql/createdb.sql
```
**Note**: Upon that, you will be asked to provide a password for the **onlyoffice**
PostgreSQL user. Please enter the **onlyoffice** password.
###### Installing RabbitMQ
Upon that, you will be asked to provide a password for the onlyoffice PostgreSQL user. Please enter the **onlyoffice** password.
#### **Install RabbitMQ**
```bash
sudo apt-get install rabbitmq-server
```
###### Generate fonts data
Now that you have all the dependencies installed, it's time to generate server files.
#### **Step 2. Generate server files**
Before running the server, you need to generate font and theme data.
##### **Generate fonts data**
```bash
cd out/linux_64/onlyoffice/documentserver/
@ -230,8 +227,7 @@ LD_LIBRARY_PATH=${PWD}/server/FileConverter/bin server/tools/allfontsgen \
--use-system="true"
```
###### Generate presentation themes
##### **Generate presentation themes**
```bash
cd out/linux_64/onlyoffice/documentserver/
LD_LIBRARY_PATH=${PWD}/server/FileConverter/bin server/tools/allthemesgen \
@ -240,27 +236,39 @@ LD_LIBRARY_PATH=${PWD}/server/FileConverter/bin server/tools/allthemesgen \
--output="${PWD}/sdkjs/common/Images"
```
##### Running Document Server
#### **Step 3. Run the Document Server services**
**Note**: All **Document Server** components run as foreground processes. Thus
you need separate terminal consoles to run them or specific tools which will
allow to run foreground processes in background mode.
All Document Server components run as foreground processes. Thus you need separate terminal consoles to run them or specific tools which will allow to run foreground processes in background mode.
1. Start the **FileConverter** service:
* **Start the FileConverter service:**
```bash
cd out/linux_64/onlyoffice/documentserver/server/FileConverter
LD_LIBRARY_PATH=$PWD/bin \
NODE_ENV=development-linux \
NODE_CONFIG_DIR=$PWD/../Common/config \
./converter
```
```bash
cd out/linux_64/onlyoffice/documentserver/server/FileConverter
LD_LIBRARY_PATH=$PWD/bin \
NODE_ENV=development-linux \
NODE_CONFIG_DIR=$PWD/../Common/config \
./converter
```
* **Start the DocService service:**
```bash
cd out/linux_64/onlyoffice/documentserver/server/DocService
NODE_ENV=development-linux \
NODE_CONFIG_DIR=$PWD/../Common/config \
./docservice
```
2. Start the **DocService** service:
## And it's a wrap! 🎉
Congratulations! You have successfully used the ```build_tools``` to compile your desired ONLYOFFICE products from the latest source code.
```bash
cd out/linux_64/onlyoffice/documentserver/server/DocService
NODE_ENV=development-linux \
NODE_CONFIG_DIR=$PWD/../Common/config \
./docservice
```
Everything is now set up. You can go ahead and run your brand-new, self-compiled ONLYOFFICE applications.
## Need help or have an idea? 💡
* **🐞 Found a bug?** Please report it by creating an [issue](https://github.com/ONLYOFFICE/build_tools/issues).
* **❓ Have a question?** Ask our community and developers on the [ONLYOFFICE Forum](https://community.onlyoffice.com).
* **💡 Want to suggest a feature?** Share your ideas on our [feedback platform](https://feedback.onlyoffice.com/forums/966080-your-voice-matters).
* **🧑‍💻 Need help for developers?** Check our [API documentation](https://api.onlyoffice.com/?utm_source=github&utm_medium=cpc&utm_campaign=GitHubBuildTools).
---
<p align="center"> Made with ❤️ by the ONLYOFFICE Team </p>

View File

@ -14,7 +14,7 @@ parser.add_option("--clean", action="store", type="string", dest="clean", defaul
parser.add_option("--module", action="store", type="string", dest="module", default="builder", help="defines what modules to build. You can specify several of them, e.g. --module 'core desktop builder server mobile'")
parser.add_option("--develop", action="store", type="string", dest="develop", default="0", help="defines develop mode")
parser.add_option("--beta", action="store", type="string", dest="beta", default="0", help="defines beta mode")
parser.add_option("--platform", action="store", type="string", dest="platform", default="native", help="defines the destination platform for your build ['win_64', 'win_32', 'win_64_xp', 'win_32_xp', 'linux_64', 'linux_32', 'mac_64', 'ios', 'android_arm64_v8a', 'android_armv7', 'android_x86', 'android_x86_64'; combinations: 'native': your current system (windows/linux/mac only); 'all': all available systems; 'windows': win_64 win_32 win_64_xp win_32_xp; 'linux': linux_64 linux_32; 'mac': mac_64; 'android': android_arm64_v8a android_armv7 android_x86 android_x86_64]")
parser.add_option("--platform", action="store", type="string", dest="platform", default="native", help="defines the destination platform for your build ['win_64', 'win_32', 'win_64_xp', 'win_32_xp', 'win_arm64', 'linux_64', 'linux_32', 'mac_64', 'ios', 'android_arm64_v8a', 'android_armv7', 'android_x86', 'android_x86_64'; combinations: 'native': your current system (windows/linux/mac only); 'all': all available systems; 'windows': win_64 win_32 win_64_xp win_32_xp; 'linux': linux_64 linux_32; 'mac': mac_64; 'android': android_arm64_v8a android_armv7 android_x86 android_x86_64]")
parser.add_option("--config", action="store", type="string", dest="config", default="", help="provides ability to specify additional parameters for qmake")
parser.add_option("--qt-dir", action="store", type="string", dest="qt-dir", default="", help="defines qmake directory path. qmake can be found in qt-dir/compiler/bin directory")
parser.add_option("--qt-dir-xp", action="store", type="string", dest="qt-dir-xp", default="", help="defines qmake directory path for Windows XP. qmake can be found in 'qt-dir/compiler/bin directory")
@ -42,6 +42,8 @@ parser.add_option("--vs-version", action="store", type="string", dest="vs-versio
parser.add_option("--vs-path", action="store", type="string", dest="vs-path", default="", help="path to vcvarsall")
parser.add_option("--siteUrl", action="store", type="string", dest="siteUrl", default="127.0.0.1", help="site url")
parser.add_option("--multiprocess", action="store", type="string", dest="multiprocess", default="1", help="provides ability to specify single process for make")
parser.add_option("--sysroot", action="store", type="string", dest="sysroot", default="0", help="provides ability to use sysroot (ubuntu 16.04) to build c++ code. If value is \"1\", then the sysroot from tools/linux/sysroot will be used, and if it is not there, it will download it and unpack it. You can also set value as the path to the your own sysroot (rarely used). Only for linux")
parser.add_option("--qemu-win-arm64-dir", action="store", type="string", dest="qemu-win-arm64-dir", default="", help="dir to qemu virtual machine for win_arm64 cross build. It should contains start.bat. More info in tools/win/qemu.")
(options, args) = parser.parse_args(arguments)
configOptions = vars(options)

View File

@ -1,3 +1,3 @@
sdkjs-plugin="photoeditor, macros, ocr, translator, thesaurus, youtube, highlightcode, zotero"
sdkjs-plugin-server="speech, zotero, mendeley, speechrecognition, drawio"
sdkjs-plugin="ai, photoeditor, ocr, translator, thesaurus, youtube, highlightcode"
sdkjs-plugin-server="speech, zotero, mendeley, speechrecognition"
sdkjs-addons="sdkjs-forms"

View File

@ -1,8 +1,8 @@
# Docker
This directory containing instruction for developers,
who want to change something in sdkjs or web-apps or server module,
but don't want to compile pretty compilcated core product to make those changes.
This directory contains instructions for developers,
who want to change something in sdkjs, web-apps, or the server module,
but don't want to compile the complicated core product to make those changes.
## System requirements
@ -25,22 +25,22 @@ You need the latest
[Docker](https://docs.docker.com/engine/install/)
version installed.
## Create develop Docker Images
## Create Development Docker Image
To create a image with the ability to include external non-minified sdkjs code,
To create an image with the ability to include external non-minified sdkjs code,
use the following commands:
### Clone development environment to work dir
### Clone development environment to the working directory
```bash
git clone https://github.com/ONLYOFFICE/build_tools.git
```
### Modify Docker Images
### Build Docker Image
**Note**: Do not prefix docker command with sudo.
[This](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user)
instruction show how to use docker without sudo.
**Note**: Do not prefix the docker command with sudo.
[These instructions](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user)
show how to use docker without sudo.
```bash
cd build_tools/develop
@ -50,11 +50,11 @@ docker build --no-cache -t documentserver-develop .
**Note**: The dot at the end is required.
**Note**: Sometimes script may fail due to network errors. Just restart it.
**Note**: Sometimes the build may fail due to network errors. Just restart it.
## Clone development modules
Clone development modules to the work dir
Clone development modules to the working directory.
* `sdkjs` repo is located [here](https://github.com/ONLYOFFICE/sdkjs/)
* `web-apps` repo is located [here](https://github.com/ONLYOFFICE/web-apps/)
@ -72,43 +72,49 @@ To mount external folders to the container,
you need to pass the "-v" parameter
along with the relative paths to the required folders.
The folders `sdkjs` and `web-apps` are required for proper development workflow.
The folders `server` is optional
The folder `server` is optional.
**Note**: Run command with the current working directory
**Note**: Run the command with the current working directory
containing `sdkjs`, `web-apps`...
**Note**: ONLYOFFICE server uses port 80.
Look for another application using port 80 and stop it
Look for another application using port 80 and stop it.
**Note**: Server start with `sdkjs` and `web-apps` takes 15 minutes
and takes 20 minutes with `server`
**Note**: Starting the server with `sdkjs` and `web-apps` takes 15 minutes,
or 20 minutes with `server`.
### docker run on Windows (PowerShell)
**Note**: Run PowerShell as administrator to fix EACCES error when installing
node_modules
node_modules.
run with `sdkjs` and `web-apps`
Run with `sdkjs` and `web-apps`
```bash
```powershell
docker run -i -t -p 80:80 --restart=always -e ALLOW_PRIVATE_IP_ADDRESS=true -v $pwd/sdkjs:/var/www/onlyoffice/documentserver/sdkjs -v $pwd/web-apps:/var/www/onlyoffice/documentserver/web-apps documentserver-develop
```
or run with `sdkjs`, `web-apps` and `server`
Or run with `sdkjs`, `web-apps`, and `server`
```powershell
docker run -i -t -p 80:80 --restart=always -e ALLOW_PRIVATE_IP_ADDRESS=true -v $pwd/sdkjs:/var/www/onlyoffice/documentserver/sdkjs -v $pwd/web-apps:/var/www/onlyoffice/documentserver/web-apps -v $pwd/server:/var/www/onlyoffice/documentserver/server documentserver-develop
```
**Note**: If using Git Bash instead of PowerShell, you may need to quote the paths:
```bash
docker run -i -t -p 80:80 --restart=always -e ALLOW_PRIVATE_IP_ADDRESS=true -v $pwd/sdkjs:/var/www/onlyoffice/documentserver/sdkjs -v $pwd/web-apps:/var/www/onlyoffice/documentserver/web-apps -v $pwd/server:/var/www/onlyoffice/documentserver/server documentserver-develop
docker run -i -t -p 80:80 --restart=always -e ALLOW_PRIVATE_IP_ADDRESS=true -v "$(pwd)/sdkjs":/var/www/onlyoffice/documentserver/sdkjs -v "$(pwd)/web-apps":/var/www/onlyoffice/documentserver/web-apps documentserver-develop
```
### docker run on Linux or macOS
run with `sdkjs` and `web-apps`
Run with `sdkjs` and `web-apps`
```bash
docker run -i -t -p 80:80 --restart=always -e ALLOW_PRIVATE_IP_ADDRESS=true -v $(pwd)/sdkjs:/var/www/onlyoffice/documentserver/sdkjs -v $(pwd)/web-apps:/var/www/onlyoffice/documentserver/web-apps documentserver-develop
```
or run with `sdkjs`, `web-apps` and `server`
Or run with `sdkjs`, `web-apps`, and `server`
```bash
docker run -i -t -p 80:80 --restart=always -e ALLOW_PRIVATE_IP_ADDRESS=true -v $(pwd)/sdkjs:/var/www/onlyoffice/documentserver/sdkjs -v $(pwd)/web-apps:/var/www/onlyoffice/documentserver/web-apps -v $(pwd)/server:/var/www/onlyoffice/documentserver/server documentserver-develop
@ -116,93 +122,99 @@ docker run -i -t -p 80:80 --restart=always -e ALLOW_PRIVATE_IP_ADDRESS=true -v $
## Open editor
After the server starts successfully, you will see Docker log messages like this
After the server starts successfully, you will see Docker log messages like this.
```bash
```text
[Date] [WARN] [localhost] [docId] [userId] nodeJS
```
To try the document editor, open a browser tab and type
[http://localhost/example](http://localhost/example) into the URL bar.
**Note**: Disable **ad blockers** for localhost page.
It may block some scripts (like Analytics.js)
**Note**: Disable **ad blockers** for the localhost page.
They may block some scripts (like Analytics.js).
## Modify sources
### To change something in `sdkjs` do the following steps
### To change something in `sdkjs`, do the following steps
1)Edit source file. Let's insert an image url into each open document.
Following command inserts (in case of problems, you can replace URL)
1) Edit the source file. Let's insert an image URL into each open document.
The following command inserts (in case of problems, you can replace the URL)
`this.AddImageUrl(['http://localhost/example/images/logo.png']);`
after event
`this.sendEvent('asc_onDocumentContentReady');`
in file
`sdkjs/common/apiBase.js`
### change sdkjs on Windows (PowerShell)
```bash
(Get-Content sdkjs/common/apiBase.js) -replace "this\.sendEvent\('asc_onDocumentContentReady'\);", "this.sendEvent('asc_onDocumentContentReady');this.AddImageUrl(['http://localhost/example/images/logo.png']);" | Set-Content sdkjs/common/apiBase.js
**Windows (PowerShell):**
```powershell
(Get-Content sdkjs/common/apiBase.js) -replace "this\.sendEvent\('asc_onDocumentContentReady'\);", "this.sendEvent('asc_onDocumentContentReady');this.AddImageUrl(['http://localhost/example/images/logo.png']);" | Set-Content sdkjs/common/apiBase.js
```
### change sdkjs on Linux or macOS
**Linux:**
```bash
sed -i "s,this.sendEvent('asc_onDocumentContentReady');,this.sendEvent('asc_onDocumentContentReady');this.AddImageUrl(['http://localhost/example/images/logo.png']);," sdkjs/common/apiBase.js
```
2)Delete browser cache or hard reload the page `Ctrl + Shift + R`
**macOS:**
```bash
sed -i '' "s,this.sendEvent('asc_onDocumentContentReady');,this.sendEvent('asc_onDocumentContentReady');this.AddImageUrl(['http://localhost/example/images/logo.png']);," sdkjs/common/apiBase.js
```
3)Open new file in browser
2) Clear the browser cache or hard reload the page (`Ctrl + Shift + R` or `Cmd + Shift + R` on macOS)
### To change something in `server` do the following steps
3) Open a new file in the browser
1)Edit source file. Let's send `"Hello World!"`
chart message every time a document is opened.
Following command inserts
### To change something in `server`, do the following steps
1) Edit the source file. Let's send a `"Hello World!"`
chat message every time a document is opened.
The following command inserts
`yield* onMessage(ctx, conn, {"message": "Hello World!"});`
in function
`sendAuthInfo`
in file
`server/DocService/sources/DocsCoServer.js`
### change server on Windows (PowerShell)
```bash
**Windows (PowerShell):**
```powershell
(Get-Content server/DocService/sources/DocsCoServer.js) -replace 'opt_hasForgotten, opt_openedAt\) \{', 'opt_hasForgotten, opt_openedAt) {yield* onMessage(ctx, conn, {"message": "Hello World!"});' | Set-Content server/DocService/sources/DocsCoServer.js
```
### change server on Linux or macOS
**Linux:**
```bash
sed -i 's#opt_hasForgotten, opt_openedAt) {#opt_hasForgotten, opt_openedAt) {yield* onMessage(ctx, conn, {"message": "Hello World!"});#' server/DocService/sources/DocsCoServer.js
```
2)Restart document server process
**macOS:**
```bash
sed -i '' 's#opt_hasForgotten, opt_openedAt) {#opt_hasForgotten, opt_openedAt) {yield* onMessage(ctx, conn, {"message": "Hello World!"});#' server/DocService/sources/DocsCoServer.js
```
**Note**: Look for ``CONTAINER_ID`` in the result of ``docker ps``.
2) Restart the document server process
**Note**: Look for `CONTAINER_ID` in the result of `docker ps`.
```bash
docker exec -it CONTAINER_ID supervisorctl restart all
```
3)Open new file in browser
3) Open a new file in the browser
## Start server with additional functionality(addons)
## Start server with additional functionality (addons)
To get additional functionality and branding you need to connect a branding folder,
additional addon folders and pass command line arguments
To get additional functionality and branding, you need to connect a branding folder,
additional addon folders, and pass command line arguments.
For example run with `onlyoffice` branding and
addons:`sdkjs-forms`, `sdkjs-ooxml`, `web-apps-mobile`
For example, run with `onlyoffice` branding and
addons: `sdkjs-forms`, `sdkjs-ooxml`, `web-apps-mobile`.
### docker run on Windows (PowerShell) with branding
**Note**: Run PowerShell as administrator to fix EACCES error when installing
node_modules
node_modules.
```bash
```powershell
docker run -i -t -p 80:80 --restart=always -e ALLOW_PRIVATE_IP_ADDRESS=true `
-v $pwd/sdkjs:/var/www/onlyoffice/documentserver/sdkjs -v $pwd/web-apps:/var/www/onlyoffice/documentserver/web-apps `
-v $pwd/onlyoffice:/var/www/onlyoffice/documentserver/onlyoffice -v $pwd/sdkjs-ooxml:/var/www/onlyoffice/documentserver/sdkjs-ooxml -v $pwd/sdkjs-forms:/var/www/onlyoffice/documentserver/sdkjs-forms -v $pwd/web-apps-mobile:/var/www/onlyoffice/documentserver/web-apps-mobile `

View File

@ -8,7 +8,7 @@ import run_server
import config
import base
git_dir = sys.argv[1];
git_dir = sys.argv[1]
base.print_info('argv :'+' '.join(sys.argv))
base.cmd_in_dir(git_dir + '/build_tools/', 'python3', ['configure.py', '--develop', '1'] + sys.argv[2:])
@ -18,7 +18,7 @@ config.parse_defaults()
if base.is_exist(git_dir + "/server/FileConverter/bin/fonts.log"):
base.print_info('remove font cache to regenerate fonts in external sdkjs volume')
base.delete_file(git_dir + "/server/FileConverter/bin/fonts.log");
base.delete_file(git_dir + "/server/FileConverter/bin/fonts.log")
# external server volume
if base.is_exist(sys.argv[1] + '/server/DocService/package.json'):
@ -28,7 +28,7 @@ if base.is_exist(sys.argv[1] + '/server/DocService/package.json'):
base.replaceInFileRE("/etc/supervisor/conf.d/ds-converter.conf", "command=.*", "command=node " + git_dir + "/server/FileConverter/sources/convertermaster.js")
base.replaceInFileRE("/app/ds/setup/config/supervisor/ds/ds-converter.conf", "command=.*", "command=node " + git_dir + "/server/FileConverter/sources/convertermaster.js")
base.print_info('run_server.run_docker_server')
run_server.run_docker_server();
run_server.run_docker_server()
else:
#Fix theme generation for external sdkjs volume
if base.is_exist(git_dir + "/server/FileConverter/bin/DoctRenderer.config"):

66
linux-start.sh Normal file
View File

@ -0,0 +1,66 @@
sudo apt-get install git curl wget p7zip-full
sudo apt-get install git-lfs
# for old system (ubuntu 16)
#curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
#sudo apt-get install git-lfs
# save login
git config --global credential.helper store
# clone build_tools
git clone https://git.onlyoffice.com/ONLYOFFICE/build_tools.git
# deps =========================================
cd ./build_tools/tools/linux
# python 3.10
./python.sh
# qt
#./python3/bin/python3 ./qt_binary_fetch.py amd64
#./python3/bin/python3 ./qt_binary_fetch.py arm64
./python3/bin/python3 ./qt_binary_fetch.py all
# deps
./python3/bin/python3 ./deps.py
# cmake 3.30
sudo ./cmake.sh
cd ../../
# ==============================================
# sysroots (IF NEEDED) =========================
cd ./build_tools/tools/linux/sysroot
#./python3/bin/python3 ./fetch.py amd64
#./python3/bin/python3 ./fetch.py arm64
./../python3/bin/python3 ./fetch.py all
cd ../../../
# ==============================================
# configure ====================================
./tools/linux/python3/bin/python3 ./configure.py --clean "0" --update-light "1" --update "1" --branch "hotfix/v9.2.1" --module "desktop" --qt-dir "$(pwd)/tools/linux/qt_build/Qt-5.9.9"
# with sysroot: sysroot "1"
# ==============================================
# cross build linux_arm64
sudo apt install qemu-user qemu-user-static binfmt-support
sudo update-binfmts --enable qemu-aarch64
# 1) without sysroot
#sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
#sudo dpkg --add-architecture arm64
#sudo apt update
#... install all dev packages ...
# 2) official supported: with sysroot
./tools/linux/python3/bin/python3 ./configure.py sysroot "1" #...

View File

@ -19,8 +19,6 @@ import make_common
import develop
import argparse
base.check_python()
parser = argparse.ArgumentParser(description="options")
parser.add_argument("--build-only-branding", action="store_true")
args = parser.parse_args()
@ -30,6 +28,7 @@ if (args.build_only_branding):
# parse configuration
config.parse()
base.check_python()
base_dir = base.get_script_dir(__file__)
@ -92,8 +91,6 @@ if config.check_option("module", "desktop"):
config.extend_option("config", "updmodule")
base.set_env("DESKTOP_URL_UPDATES_MAIN_CHANNEL", "https://download.onlyoffice.com/install/desktop/editors/windows/onlyoffice/appcast.json")
base.set_env("DESKTOP_URL_UPDATES_DEV_CHANNEL", "https://download.onlyoffice.com/install/desktop/editors/windows/onlyoffice/appcastdev.json")
base.set_env("DESKTOP_URL_INSTALL_CHANNEL", "https://download.onlyoffice.com/install/desktop/editors/windows/distrib/onlyoffice/<file>")
base.set_env("DESKTOP_URL_INSTALL_DEV_CHANNEL", "https://download.onlyoffice.com/install/desktop/editors/windows/onlyoffice/onlineinstallerdev/<file>")
# build
build_sln.make()

View File

@ -15,6 +15,8 @@ import stat
import json
__file__script__path__ = os.path.dirname( os.path.realpath(__file__))
icu_ver = "74"
icu_ver_old = "58" # for win_xp support
# common functions --------------------------------------
def get_script_dir(file=""):
@ -35,9 +37,11 @@ def is_os_64bit():
return platform.machine().endswith('64')
def is_os_arm():
if -1 == platform.machine().find('arm'):
return False
return True
if -1 != platform.machine().lower().find('arm'):
return True
if -1 != platform.machine().lower().find('aarch64'):
return True
return False
def get_platform():
return platform.machine().lower()
@ -72,7 +76,7 @@ def check_build_version(dir):
version_number = version_number.replace("\n", "")
set_env("PRODUCT_VERSION", version_number)
if ("" == get_env("BUILD_NUMBER")):
set_env("BUILD_NUMBER", "0")
set_env("BUILD_NUMBER", "0")
return
def print_info(info=""):
@ -196,11 +200,11 @@ def move_dir(src, dst):
delete_dir(src)
return
def copy_dir(src, dst):
def copy_dir(src, dst, symlinks=False):
if is_dir(dst):
delete_dir(dst)
try:
shutil.copytree(get_path(src), get_path(dst))
shutil.copytree(get_path(src), get_path(dst), symlinks=symlinks)
except:
if ("windows" == host_platform()) and copy_dir_windows(src, dst):
return
@ -252,9 +256,12 @@ def delete_dir(path):
shutil.rmtree(get_path(path), ignore_errors=True)
return
def copy_lib(src, dst, name):
def copy_lib(src_folder, dst, name, subdir=""):
src = src_folder
if subdir != "":
src += ("/" + subdir)
if (config.check_option("config", "bundle_dylibs")) and is_dir(src + "/" + name + ".framework"):
copy_dir(src + "/" + name + ".framework", dst + "/" + name + ".framework")
copy_dir(src + "/" + name + ".framework", dst + "/" + name + ".framework", symlinks=True)
if (config.check_option("config", "bundle_xcframeworks")) and is_dir(src + "/simulator/" + name + ".framework"):
create_dir(dst + "/simulator")
@ -263,9 +270,9 @@ def copy_lib(src, dst, name):
if is_dir(dst + "/" + name + ".xcframework"):
delete_dir(dst + "/" + name + ".xcframework")
cmd("xcodebuild", ["-create-xcframework",
"-framework", dst + "/" + name + ".framework",
"-framework", dst + "/simulator/" + name + ".framework",
cmd("xcodebuild", ["-create-xcframework",
"-framework", dst + "/" + name + ".framework",
"-framework", dst + "/simulator/" + name + ".framework",
"-output", dst + "/" + name + ".xcframework"])
delete_dir(dst + "/" + name + ".framework")
@ -368,7 +375,7 @@ def writeFile(path, data):
return
# system cmd methods ------------------------------------
def cmd(prog, args=[], is_no_errors=False):
def cmd(prog, args=[], is_no_errors=False):
ret = 0
if ("windows" == host_platform()):
sub_args = args[:]
@ -383,7 +390,7 @@ def cmd(prog, args=[], is_no_errors=False):
sys.exit("Error (" + prog + "): " + str(ret))
return ret
def cmd2(prog, args=[], is_no_errors=False):
def cmd2(prog, args=[], is_no_errors=False):
ret = 0
command = prog if ("windows" != host_platform()) else get_path(prog)
for arg in args:
@ -429,11 +436,46 @@ def cmd_in_dir(directory, prog, args=[], is_no_errors=False):
return ret
def cmd_in_dir_qemu(platform, directory, prog, args=[], is_no_errors=False):
if (platform == "linux_arm64"):
return cmd_in_dir(directory, "qemu-aarch64", ["-L", "/usr/aarch64-linux-gnu", prog] + args, is_no_errors)
if (platform == "linux_arm32"):
return cmd_in_dir(directory, "qemu-arm", ["-L", "/usr/arm-linux-gnueabi", prog] + args, is_no_errors)
return 0
platform_config = {
"linux_arm64": {
"qemu": "qemu-aarch64",
"default_libs": "/usr/aarch64-linux-gnu"
},
"linux_arm32": {
"qemu": "qemu-arm",
"default_libs": "/usr/arm-linux-gnueabi"
}
}
if platform not in platform_config:
return 0
libs_path = platform_config[platform]["default_libs"]
if config.option("sysroot") != "":
libs_path = config.option("sysroot_" + platform)
return cmd_in_dir(directory, platform_config[platform]["qemu"], ["-L", libs_path, prog] + args, is_no_errors)
def create_qemu_wrapper(binary_path, platform):
binary_dir = os.path.dirname(binary_path)
binary_name = os.path.basename(binary_path)
binary_bin = binary_path + '.bin'
sysroot = config.option("sysroot_" + platform)
if os.path.exists(binary_path):
os.rename(binary_path, binary_bin)
wrapper_content = f'''#!/bin/bash
DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
export QEMU_LD_PREFIX={sysroot}
exec qemu-aarch64 -L {sysroot} "$DIR/{binary_name}.bin" "$@"
'''
with open(binary_path, 'w') as f:
f.write(wrapper_content)
os.chmod(binary_path, 0o755)
return binary_bin
def cmd_and_return_cwd(prog, args=[], is_no_errors=False):
cur_dir = os.getcwd()
@ -453,7 +495,7 @@ def run_command(sCommand):
finally:
popen.stdout.close()
popen.stderr.close()
return result
def run_command_in_dir(directory, sCommand):
@ -468,7 +510,7 @@ def run_command_in_dir(directory, sCommand):
if (host == 'windows'):
os.chdir(cur_dir)
return ret
def exec_command_in_dir(directory, sCommand):
host = host_platform()
if (host == 'windows'):
@ -521,6 +563,27 @@ def git_get_origin():
os.chdir(cur_dir)
return ret
def git_get_base_url():
"""Get the base URL for git operations, with fallback to GitHub"""
origin = git_get_origin()
if origin:
# Extract base URL from origin
if origin.startswith("https://"):
# For HTTPS URLs like https://git.example.com/owner/repo.git
parts = origin.split("/")
if len(parts) >= 4:
return "/".join(parts[:3]) + "/"
elif ":" in origin and "@" in origin:
# For SSH URLs like git@git.example.com:owner/repo.git
at_pos = origin.find("@")
colon_pos = origin.find(":", at_pos)
if at_pos != -1 and colon_pos != -1:
host = origin[at_pos+1:colon_pos]
return f"https://{host}/"
# Fallback to GitHub
return "https://github.com/"
def git_is_ssh():
git_protocol = config.option("git-protocol")
if (git_protocol == "https"):
@ -542,7 +605,7 @@ def get_ssh_base_url():
def git_update(repo, is_no_errors=False, is_current_dir=False, git_owner=""):
print("[git] update: " + repo)
owner = git_owner if git_owner else "ONLYOFFICE"
url = "https://github.com/" + owner + "/" + repo + ".git"
url = git_get_base_url() + owner + "/" + repo + ".git"
if git_is_ssh():
url = get_ssh_base_url() + repo + ".git"
folder = get_script_dir() + "/../../" + repo
@ -596,6 +659,7 @@ def get_repositories():
result.update(get_server_addons())
result["document-server-integration"] = [False, False]
result["document-templates"] = [False, False]
result["document-formats"] = [False, False]
get_branding_repositories(result)
return result
@ -614,7 +678,7 @@ def get_branding_repositories(checker):
def create_pull_request(branches_to, repo, is_no_errors=False, is_current_dir=False):
print("[git] create pull request: " + repo)
url = "https://github.com/ONLYOFFICE/" + repo + ".git"
url = git_get_base_url() + "ONLYOFFICE/" + repo + ".git"
if git_is_ssh():
url = get_ssh_base_url() + repo + ".git"
folder = get_script_dir() + "/../../" + repo
@ -641,7 +705,7 @@ def create_pull_request(branches_to, repo, is_no_errors=False, is_current_dir=Fa
cmd("git", ["merge", "--abort"], is_no_errors)
else:
cmd("git", ["push"], is_no_errors)
os.chdir(old_cur)
return
@ -653,7 +717,7 @@ def update_repositories(repositories):
git_update(repo, value[0], False)
else:
if is_dir(current_dir + "/.git"):
delete_dir_with_access_error(current_dir);
delete_dir_with_access_error(current_dir)
delete_dir(current_dir)
if not is_dir(current_dir):
create_dir(current_dir)
@ -666,18 +730,28 @@ def git_dir():
if ("windows" == host_platform()):
return run_command("git --info-path")['stdout'] + "/../../.."
def get_prefix_cross_compiler_arm64():
cross_compiler_arm64 = config.option("arm64-toolchain-bin")
if is_file(cross_compiler_arm64 + "/aarch64-linux-gnu-g++") and is_file(cross_compiler_arm64 + "/aarch64-linux-gnu-gcc"):
return "aarch64-linux-gnu-"
if is_file(cross_compiler_arm64 + "/aarch64-unknown-linux-gnu-g++") and is_file(cross_compiler_arm64 + "/aarch64-unknown-linux-gnu-gcc"):
return "aarch64-unknown-linux-gnu-"
return ""
def get_compiler_gcc_prefix(platform):
directory = "/usr/bin"
if config.option("sysroot") != "":
use_platform = platform
if ("linux_arm64" == platform and not is_os_arm()):
use_platform = "linux_64"
directory = config.option("sysroot_" + use_platform) + "/usr/bin"
if ("linux_arm64" == platform and not is_os_arm()):
return directory + "/aarch64-linux-gnu-"
return directory + "/"
def get_gcc_version():
# if use sysroot - fix gcc version
if config.option("sysroot") != "":
return 5004
gcc_path = "gcc"
gcc_version_major = 4
gcc_version_minor = 0
gcc_version_str = run_command("gcc -dumpfullversion -dumpversion")['stdout']
gcc_version_str = run_command(gcc_path + " -dumpfullversion -dumpversion")['stdout']
if (gcc_version_str != ""):
try:
gcc_ver = gcc_version_str.split(".")
@ -715,15 +789,16 @@ def qt_setup(platform):
if ("gcc_arm" == compiler_platform):
qt_dir = config.option("qt-dir") + "/gcc"
# OVERRIDE IF NEEDED
set_env("QT_QMAKE_ADDON", "")
if platform == "win_arm64" and not is_dir(qt_dir):
override_qt_directory = os.path.abspath(os.path.dirname(__file__) + "/../tools/win/arm64/qt_build/Qt-5.15.2/win_arm64")
if is_dir(override_qt_directory):
qt_dir = os.path.abspath(override_qt_directory).replace("\\", "/")
set_env("QT_QMAKE_ADDON", "-spec win32-arm64-msvc2017")
set_env("QT_DEPLOY", qt_dir + "/bin")
if ("linux_arm64" == platform):
cross_compiler_arm64 = config.option("arm64-toolchain-bin")
if ("" != cross_compiler_arm64):
set_env("ARM64_TOOLCHAIN_BIN", cross_compiler_arm64)
set_env("ARM64_TOOLCHAIN_BIN_PREFIX", get_prefix_cross_compiler_arm64())
return qt_dir
return qt_dir
def qt_version():
qt_dir = get_env("QT_DEPLOY")
@ -809,6 +884,9 @@ def qt_config(platform):
if ("linux_arm64" == platform):
config_param += " linux_arm64"
if ("win_arm64" == platform):
config_param += " win_arm64"
config_param += qt_config_platform_addon(platform)
return config_param
@ -833,6 +911,12 @@ def qt_config_as_param(value):
def qt_copy_lib(lib, dir):
qt_dir = get_env("QT_DEPLOY")
# TODO: remove version from library name
qt_major = qt_major_version()
if ("5" != qt_major):
lib = lib.replace("Qt5", "Qt" + qt_major)
if ("windows" == host_platform()):
if ("" == qt_dst_postfix()):
copy_lib(qt_dir, dir, lib)
@ -841,7 +925,7 @@ def qt_copy_lib(lib, dir):
else:
src_file = qt_dir + "/../lib/lib" + lib + ".so." + qt_version()
if (is_file(src_file)):
copy_file(src_file, dir + "/lib" + lib + ".so." + qt_major_version())
copy_file(src_file, dir + "/lib" + lib + ".so." + qt_major)
else:
libFramework = lib
libFramework = libFramework.replace("Qt5", "Qt")
@ -854,20 +938,40 @@ def qt_copy_lib(lib, dir):
def _check_icu_common(dir, out):
isExist = False
for file in glob.glob(dir + "/libicu*"):
isExist = True
break
# Skip static libraries
if not file.endswith('.a'):
isExist = True
break
if isExist:
copy_files(dir + "/libicui18n*", out)
copy_files(dir + "/libicuuc*", out)
copy_files(dir + "/libicudata*", out)
# Copy only shared libraries (skip .a files)
for pattern in ["/libicui18n*", "/libicuuc*", "/libicudata*"]:
for file in glob.glob(dir + pattern):
if not file.endswith('.a'):
copy_file(file, out)
return isExist
def qt_copy_icu(out):
tests = [get_env("QT_DEPLOY") + "/../lib", "/lib", "/lib/x86_64-linux-gnu", "/lib64", "/lib64/x86_64-linux-gnu"]
tests += ["/usr/lib", "/usr/lib/x86_64-linux-gnu", "/usr/lib64", "/usr/lib64/x86_64-linux-gnu"]
tests += ["/lib/i386-linux-gnu", "/usr/lib/i386-linux-gnu"]
def qt_copy_icu(out, platform):
tests = [get_env("QT_DEPLOY") + "/../lib"]
prefix = ""
postfixes = [""]
if config.option("sysroot_" + platform) != "":
prefix = config.option("sysroot_" + platform)
else:
prefix = ""
if ("linux_64" == platform):
postfixes += ["/x86_64-linux-gnu"]
elif ("linux_arm64" == platform):
postfixes += ["/aarch64-linux-gnu"]
elif ("linux_32" == platform):
postfixes += ["/i386-linux-gnu"]
for postfix in postfixes:
tests += [prefix + "/lib" + postfix]
tests += [prefix + "/lib64" + postfix]
tests += [prefix + "/usr/lib" + postfix]
tests += [prefix + "/usr/lib64" + postfix]
for test in tests:
if (_check_icu_common(test, out)):
@ -879,7 +983,7 @@ def qt_copy_plugin(name, out):
src = get_env("QT_DEPLOY") + "/../plugins/" + name
if not is_dir(src):
return
copy_dir(src, out + "/" + name)
if ("windows" == host_platform()):
@ -891,7 +995,7 @@ def qt_copy_plugin(name, out):
else:
delete_file(fileCheck)
for file in glob.glob(out + "/" + name + "/*.pdb"):
delete_file(file)
delete_file(file)
return
def qt_dst_postfix():
@ -956,14 +1060,14 @@ def generate_doctrenderer_config(path, root, product, vendor = "", dictionaries
file.close()
return
def generate_plist_framework_folder(file):
def generate_plist(file, platform):
bundle_id_url = "com.onlyoffice."
if ("" != get_env("PUBLISHER_BUNDLE_ID")):
bundle_id_url = get_env("PUBLISHER_BUNDLE_ID")
bundle_creator = "Ascensio System SIA"
if ("" != get_env("PUBLISHER_NAME")):
bundle_creator = get_env("PUBLISHER_NAME")
bundle_version_natural = readFile(get_script_dir() + "/../../core/Common/version.txt").split(".")
bundle_version = []
for n in bundle_version_natural:
@ -990,11 +1094,14 @@ def generate_plist_framework_folder(file):
content += "\t<string>????</string>\n"
content += "\t<key>CFBundleVersion</key>\n"
content += "\t<string>" + bundle_version[0] + "." + bundle_version[1] + "." + bundle_version[2] + "</string>\n"
content += "\t<key>MinimumOSVersion</key>\n"
content += "\t<string>13.0</string>\n"
if platform.find("ios") == 0:
content += "\t<key>MinimumOSVersion</key>\n"
content += "\t<string>13.0</string>\n"
content += "</dict>\n"
content += "</plist>"
if platform.find("mac") == 0:
file += "/Resources"
fileDst = file + "/Info.plist"
if is_file(fileDst):
delete_file(fileDst)
@ -1004,7 +1111,33 @@ def generate_plist_framework_folder(file):
fileInfo.close()
return
def generate_plist(path):
def generate_xcprivacy(file, platform):
content = \
"""<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
\t<key>NSPrivacyTracking</key>
\t<false/>
\t<key>NSPrivacyCollectedDataTypes</key>
\t<array/>
\t<key>NSPrivacyTrackingDomains</key>
\t<array/>
\t<key>NSPrivacyAccessedAPITypes</key>
\t<array/>
</dict>
</plist>"""
fileDst = os.path.join(file, "PrivacyInfo.xcprivacy")
fileInfo = codecs.open(fileDst, "w", "utf-8")
fileInfo.write(content)
fileInfo.close()
return
def for_each_framework(path, platform, callbacks, max_depth=512):
if not config.check_option("config", "bundle_dylibs"):
return
if max_depth == 0:
return
src_folder = path
if ("/" != path[-1:]):
src_folder += "/"
@ -1012,9 +1145,10 @@ def generate_plist(path):
for file in glob.glob(src_folder):
if (is_dir(file)):
if file.endswith(".framework"):
generate_plist_framework_folder(file)
for callback in callbacks:
callback(file, platform)
else:
generate_plist(file)
for_each_framework(file, platform, callbacks, max_depth - 1)
return
def correct_bundle_identifier(bundle_identifier):
@ -1213,71 +1347,100 @@ def get_file_last_modified_url(url):
key = key.upper()
if key == "LAST-MODIFIED":
retvalue = value
return retvalue
def mac_change_rpath_binary(bin, old, new):
cmd("install_name_tool", ["-change", old, new, bin], True)
def mac_change_rpath_library(lib_name, old, new):
# converts library name to actual library file name (dylib or binary file in framework)
def lib_name_to_file_name(lib_name):
if config.check_option("config", "bundle_dylibs"):
lib = lib_name + ".framework/" + lib_name
else:
lib = "lib" + lib_name + ".dylib"
return lib
mac_change_rpath_binary(lib_name_to_file_name(lib_name), old, new)
def mac_correct_rpath_binary(path, libs):
# if framework are built, instead of correcting lib paths add `@loader_path` to rpaths with `mac_add_loader_path_to_rpath()`
try:
if config.check_option("config", "bundle_dylibs"):
return
except:
return
for lib in libs:
cmd("install_name_tool", ["-change", "lib" + lib + ".dylib", "@rpath/lib" + lib + ".dylib", path], True)
mac_change_rpath_binary(path, "lib" + lib + ".dylib", "@rpath/lib" + lib + ".dylib")
return
def mac_correct_rpath_library(name, libs):
return mac_correct_rpath_binary("./lib" + name + ".dylib", libs)
mac_icu_libs = ["icudata." + icu_ver, "icuuc." + icu_ver]
def mac_correct_rpath_x2t(dir):
cur_dir = os.getcwd()
os.chdir(dir)
mac_correct_rpath_library("icudata.58", [])
mac_correct_rpath_library("icuuc.58", ["icudata.58"])
mac_correct_rpath_library("UnicodeConverter", ["icuuc.58", "icudata.58"])
mac_correct_rpath_library("icudata." + icu_ver, [])
mac_correct_rpath_library("icuuc." + icu_ver, ["icudata." + icu_ver])
mac_correct_rpath_library("UnicodeConverter", mac_icu_libs)
mac_correct_rpath_library("kernel", ["UnicodeConverter"])
mac_correct_rpath_library("kernel_network", ["UnicodeConverter", "kernel"])
mac_correct_rpath_library("graphics", ["UnicodeConverter", "kernel"])
mac_correct_rpath_library("doctrenderer", ["UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "DjVuFile", "DocxRenderer"])
mac_correct_rpath_library("doctrenderer", ["UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "DocxRenderer"])
mac_correct_rpath_library("HtmlFile2", ["UnicodeConverter", "kernel", "kernel_network", "graphics"])
mac_correct_rpath_library("EpubFile", ["UnicodeConverter", "kernel", "HtmlFile2", "graphics"])
mac_correct_rpath_library("Fb2File", ["UnicodeConverter", "kernel", "graphics"])
mac_correct_rpath_library("PdfFile", ["UnicodeConverter", "kernel", "graphics", "kernel_network"])
mac_correct_rpath_library("DjVuFile", ["UnicodeConverter", "kernel", "graphics", "PdfFile"])
mac_correct_rpath_library("XpsFile", ["UnicodeConverter", "kernel", "graphics", "PdfFile"])
mac_correct_rpath_library("OFDFile", ["UnicodeConverter", "kernel", "graphics", "PdfFile"])
mac_correct_rpath_library("DocxRenderer", ["UnicodeConverter", "kernel", "graphics"])
mac_correct_rpath_library("IWorkFile", ["UnicodeConverter", "kernel"])
mac_correct_rpath_library("HWPFile", ["UnicodeConverter", "kernel", "graphics"])
cmd("chmod", ["-v", "+x", "./x2t"])
cmd("install_name_tool", ["-add_rpath", "@executable_path", "./x2t"], True)
mac_correct_rpath_binary("./x2t", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "doctrenderer", "DocxRenderer", "IWorkFile", "HWPFile"])
mac_correct_rpath_library("HWPFile", ["UnicodeConverter", "kernel", "graphics", "StarMathConverter"])
mac_correct_rpath_library("StarMathConverter", ["kernel"])
mac_correct_rpath_library("ooxmlsignature", ["kernel"])
def correct_core_executable(name, libs):
cmd("chmod", ["-v", "+x", name])
cmd("install_name_tool", ["-add_rpath", "@executable_path", name], True)
mac_correct_rpath_binary(name, mac_icu_libs + libs)
return
correct_core_executable("x2t", ["UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "doctrenderer", "DocxRenderer", "IWorkFile", "HWPFile", "StarMathConverter", "ooxmlsignature"])
if is_file("./allfontsgen"):
cmd("chmod", ["-v", "+x", "./allfontsgen"])
cmd("install_name_tool", ["-add_rpath", "@executable_path", "./allfontsgen"], True)
mac_correct_rpath_binary("./allfontsgen", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "graphics"])
correct_core_executable("allfontsgen", ["UnicodeConverter", "kernel", "graphics"])
if is_file("./allthemesgen"):
cmd("chmod", ["-v", "+x", "./allthemesgen"])
cmd("install_name_tool", ["-add_rpath", "@executable_path", "./allthemesgen"], True)
mac_correct_rpath_binary("./allthemesgen", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "graphics", "kernel_network", "doctrenderer", "PdfFile", "XpsFile", "DjVuFile", "DocxRenderer"])
correct_core_executable("allthemesgen", ["UnicodeConverter", "kernel", "graphics", "kernel_network", "doctrenderer", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "DocxRenderer"])
if is_file("./pluginsmanager"):
cmd("chmod", ["-v", "+x", "./pluginsmanager"])
cmd("install_name_tool", ["-add_rpath", "@executable_path", "./pluginsmanager"], True)
mac_correct_rpath_binary("./pluginsmanager", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "kernel_network"])
correct_core_executable("pluginsmanager", ["UnicodeConverter", "kernel", "kernel_network"])
if is_file("./vboxtester"):
cmd("chmod", ["-v", "+x", "./vboxtester"])
cmd("install_name_tool", ["-add_rpath", "@executable_path", "./vboxtester"], True)
mac_correct_rpath_binary("./vboxtester", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "kernel_network"])
correct_core_executable("vboxtester", ["UnicodeConverter", "kernel", "kernel_network"])
if is_file("./x2ttester"):
correct_core_executable("x2ttester", ["UnicodeConverter", "kernel", "graphics"])
os.chdir(cur_dir)
return
def mac_add_loader_path_to_rpath(libs):
for lib in libs:
if config.check_option("config", "bundle_dylibs"):
# icu libs are linked statically for frameworks
if lib in mac_icu_libs:
continue
cmd("install_name_tool", ["-add_rpath", "@loader_path/../../..", lib + ".framework/" + lib], True)
else:
cmd("install_name_tool", ["-add_rpath", "@loader_path", "lib" + lib + ".dylib"], True)
def mac_correct_rpath_docbuilder(dir):
cur_dir = os.getcwd()
os.chdir(dir)
cmd("chmod", ["-v", "+x", "./docbuilder"])
cmd("install_name_tool", ["-add_rpath", "@executable_path", "./docbuilder"], True)
mac_correct_rpath_binary("./docbuilder", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "IWorkFile", "HWPFile", "doctrenderer", "DocxRenderer"])
mac_correct_rpath_library("docbuilder.c", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "kernel_network", "graphics", "doctrenderer", "PdfFile", "XpsFile", "DjVuFile", "DocxRenderer"])
mac_correct_rpath_binary("./docbuilder", mac_icu_libs + ["UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "IWorkFile", "HWPFile", "doctrenderer", "DocxRenderer"])
mac_correct_rpath_library("docbuilder.c", mac_icu_libs + ["UnicodeConverter", "kernel", "kernel_network", "graphics", "doctrenderer", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "DocxRenderer"])
def add_loader_path_to_rpath(libs):
for lib in libs:
cmd("install_name_tool", ["-add_rpath", "@loader_path", "lib" + lib + ".dylib"], True)
add_loader_path_to_rpath(["icuuc.58", "UnicodeConverter", "kernel", "kernel_network", "graphics", "doctrenderer", "PdfFile", "XpsFile", "DjVuFile", "DocxRenderer", "docbuilder.c"])
mac_add_loader_path_to_rpath(["icuuc." + icu_ver, "UnicodeConverter", "kernel", "kernel_network", "graphics", "doctrenderer", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "DocxRenderer", "docbuilder.c"])
os.chdir(cur_dir)
return
@ -1287,9 +1450,9 @@ def mac_correct_rpath_desktop(dir):
os.chdir(dir)
mac_correct_rpath_library("hunspell", [])
mac_correct_rpath_library("ooxmlsignature", ["kernel"])
mac_correct_rpath_library("ascdocumentscore", ["UnicodeConverter", "kernel", "graphics", "kernel_network", "PdfFile", "XpsFile", "DjVuFile", "hunspell", "ooxmlsignature"])
cmd("install_name_tool", ["-change", "@executable_path/../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework", "@rpath/Chromium Embedded Framework.framework/Chromium Embedded Framework", "libascdocumentscore.dylib"])
mac_correct_rpath_binary("./editors_helper.app/Contents/MacOS/editors_helper", ["ascdocumentscore", "UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "DjVuFile", "hunspell", "ooxmlsignature"])
mac_correct_rpath_library("ascdocumentscore", ["UnicodeConverter", "kernel", "graphics", "kernel_network", "PdfFile", "XpsFile", "DjVuFile", "hunspell", "ooxmlsignature", "doctrenderer"])
mac_change_rpath_library("ascdocumentscore", "@executable_path/../Frameworks/Chromium Embedded Framework.framework/Chromium Embedded Framework", "@rpath/Chromium Embedded Framework.framework/Chromium Embedded Framework")
mac_correct_rpath_binary("./editors_helper.app/Contents/MacOS/editors_helper", ["ascdocumentscore", "UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "hunspell", "ooxmlsignature", "doctrenderer"])
cmd("install_name_tool", ["-add_rpath", "@executable_path/../../../../Frameworks", "./editors_helper.app/Contents/MacOS/editors_helper"], True)
cmd("install_name_tool", ["-add_rpath", "@executable_path/../../../../Resources/converter", "./editors_helper.app/Contents/MacOS/editors_helper"], True)
cmd("chmod", ["-v", "+x", "./editors_helper.app/Contents/MacOS/editors_helper"])
@ -1323,7 +1486,7 @@ def linux_set_origin_rpath_libraries(dir, libs):
return
def linux_correct_rpath_docbuilder(dir):
linux_set_origin_rpath_libraries(dir, ["docbuilder.jni.so", "docbuilder.c.so", "icuuc.so.58", "doctrenderer.so", "graphics.so", "kernel.so", "kernel_network.so", "UnicodeConverter.so", "PdfFile.so", "XpsFile.so", "DjVuFile.so", "DocxRenderer.so"])
linux_set_origin_rpath_libraries(dir, ["docbuilder.jni.so", "docbuilder.c.so", "icuuc.so." + icu_ver, "doctrenderer.so", "graphics.so", "kernel.so", "kernel_network.so", "UnicodeConverter.so", "PdfFile.so", "XpsFile.so", "OFDFile.so", "DjVuFile.so", "DocxRenderer.so"])
return
def common_check_version(name, good_version, clean_func):
@ -1406,12 +1569,12 @@ def copy_sdkjs_plugins(dst_dir, is_name_as_guid=False, is_desktop_local=False, i
plugins_dir = __file__script__path__ + "/../../onlyoffice.github.io/sdkjs-plugins/content"
plugins_list_config = config.option("sdkjs-plugin")
if isXp:
plugins_list_config="photoeditor, macros, highlightcode, doc2md"
plugins_list_config="photoeditor, highlightcode, doc2md"
if ("" == plugins_list_config):
return
plugins_list = plugins_list_config.rsplit(", ")
for name in plugins_list:
copy_sdkjs_plugin(plugins_dir, dst_dir, name, is_name_as_guid, is_desktop_local)
copy_sdkjs_plugin(plugins_dir, dst_dir, name, is_name_as_guid, is_desktop_local)
return
def copy_sdkjs_plugins_server(dst_dir, is_name_as_guid=False, is_desktop_local=False):
@ -1421,7 +1584,7 @@ def copy_sdkjs_plugins_server(dst_dir, is_name_as_guid=False, is_desktop_local=F
return
plugins_list = plugins_list_config.rsplit(", ")
for name in plugins_list:
copy_sdkjs_plugin(plugins_dir, dst_dir, name, is_name_as_guid, is_desktop_local)
copy_sdkjs_plugin(plugins_dir, dst_dir, name, is_name_as_guid, is_desktop_local)
return
def support_old_versions_plugins(out_dir):
@ -1435,11 +1598,11 @@ def support_old_versions_plugins(out_dir):
content_plugin_base += file.read()
content_plugin_base += "\n\n"
with open(get_path(out_dir + "/plugins-ui.js"), "r") as file:
content_plugin_base += file.read()
content_plugin_base += file.read()
with open(get_path(out_dir + "/pluginBase.js"), "w") as file:
file.write(content_plugin_base)
delete_file(out_dir + "/plugins.js")
delete_file(out_dir + "/plugins-ui.js")
delete_file(out_dir + "/plugins-ui.js")
return
def generate_sdkjs_plugin_list(dst):
@ -1472,12 +1635,18 @@ def hack_xcode_ios():
filedata += "\n"
filedata += content_hack
filedata += "\n\n"
delete_file(qmake_spec_file)
with open(get_path(qmake_spec_file), "w") as file:
file.write(filedata)
return
def find_ios_sdk(sdk_name):
return run_command("xcrun --sdk " + sdk_name + " --show-sdk-path")['stdout']
def find_xcode_toolchain(sdk_name):
return run_command("xcrun --sdk " + sdk_name + " --show-toolchain-path")['stdout']
def find_mac_sdk_version():
sdk_dir = run_command("xcode-select -print-path")['stdout']
sdk_dir = os.path.join(sdk_dir, "Platforms/MacOSX.platform/Developer/SDKs")
@ -1552,13 +1721,16 @@ def replaceFileLicence(path, license):
def copy_v8_files(core_dir, deploy_dir, platform, is_xp=False):
if (-1 != config.option("config").find("use_javascript_core")):
return
if (0 == platform.find("mac")) and not (config.check_option("config", "use_v8")):
return
directory_v8 = core_dir + "/Common/3dParty"
if is_xp:
directory_v8 += "/v8/v8_xp"
copy_files(directory_v8 + platform + "/release/icudt*.dll", deploy_dir + "/")
return
if config.check_option("config", "v8_version_60"):
directory_v8 += "/v8/v8/out.gn/"
else:
@ -1570,7 +1742,7 @@ def copy_v8_files(core_dir, deploy_dir, platform, is_xp=False):
copy_files(directory_v8 + platform + "/icudt*.dat", deploy_dir + "/")
return
def clone_marketplace_plugin(out_dir, is_name_as_guid=False, is_replace_paths=False, is_delete_git_dir=True, git_owner=""):
def clone_marketplace_plugin(out_dir, is_name_as_guid=False, is_replace_paths=False, is_delete_git_dir=True, git_owner=""):
old_cur = os.getcwd()
os.chdir(out_dir)
git_update("onlyoffice.github.io", False, True, git_owner)
@ -1591,11 +1763,11 @@ def clone_marketplace_plugin(out_dir, is_name_as_guid=False, is_replace_paths=Fa
if is_dir(dst_dir_path):
delete_dir(dst_dir_path)
copy_dir(out_dir + "/onlyoffice.github.io/store/plugin", dst_dir_path)
if is_replace_paths:
for file in glob.glob(dst_dir_path + "/*.html"):
replaceInFile(file, "https://onlyoffice.github.io/sdkjs-plugins/", "../")
if is_delete_git_dir:
delete_dir_with_access_error(out_dir + "/onlyoffice.github.io")
return
@ -1620,7 +1792,7 @@ def restorePathForBuilder(new_path):
old_path = new_path[:-4]
delete_file(old_path)
copy_file(new_path, old_path)
delete_file(new_path);
delete_file(new_path)
return
def generate_check_linux_system(build_tools_dir, out_dir):
@ -1632,20 +1804,20 @@ def generate_check_linux_system(build_tools_dir, out_dir):
def convert_ios_framework_to_xcframework(folder, lib):
cur_dir = os.getcwd()
os.chdir(folder)
create_dir(lib + "_xc_tmp")
create_dir(lib + "_xc_tmp/iphoneos")
create_dir(lib + "_xc_tmp/iphonesimulator")
copy_dir(lib + ".framework", lib + "_xc_tmp/iphoneos/" + lib + ".framework")
copy_dir(lib + ".framework", lib + "_xc_tmp/iphonesimulator/" + lib + ".framework")
cmd("xcrun", ["lipo", "-remove", "x86_64", "./" + lib + "_xc_tmp/iphoneos/" + lib + ".framework/" + lib,
cmd("xcrun", ["lipo", "-remove", "x86_64", "./" + lib + "_xc_tmp/iphoneos/" + lib + ".framework/" + lib,
"-o", "./" + lib + "_xc_tmp/iphoneos/" + lib + ".framework/" + lib])
cmd("xcrun", ["lipo", "-remove", "arm64", "./" + lib + "_xc_tmp/iphonesimulator/" + lib + ".framework/" + lib,
cmd("xcrun", ["lipo", "-remove", "arm64", "./" + lib + "_xc_tmp/iphonesimulator/" + lib + ".framework/" + lib,
"-o", "./" + lib + "_xc_tmp/iphonesimulator/" + lib + ".framework/" + lib])
cmd("xcodebuild", ["-create-xcframework",
"-framework", "./" + lib + "_xc_tmp/iphoneos/" + lib + ".framework/",
cmd("xcodebuild", ["-create-xcframework",
"-framework", "./" + lib + "_xc_tmp/iphoneos/" + lib + ".framework/",
"-framework", "./" + lib + "_xc_tmp/iphonesimulator/" + lib + ".framework/",
"-output", lib + ".xcframework"])
@ -1661,7 +1833,7 @@ def convert_ios_framework_to_xcframework_folder(folder, libs):
def change_elf_rpath(path, origin):
# excludes ---
if (-1 != path.find("libicudata.so.58")):
if (-1 != path.find("libicudata.so." + icu_ver)):
return
# ------------
tools_dir = get_script_dir() + "/../tools/linux/elf/"
@ -1693,7 +1865,7 @@ def change_elf_rpath(path, origin):
cmd(tools_dir + "patchelf", ["--set-rpath", new_path, path], True)
#print("[" + os.path.basename(path) + "] old: " + old_path + "; new: " + new_path)
return
def correct_elf_rpath_directory(directory, origin, is_recursion = True):
for file in glob.glob(directory + "/*"):
if is_file(file):
@ -1746,14 +1918,14 @@ def copy_dictionaries(src, dst, is_hyphen = True, is_spell = True):
if is_hyphen and is_hyphen_present:
copy_dir_content(file, lang_folder, "hyph_", "")
if is_spell and is_spell_present:
copy_dir_content(file, lang_folder, "", "hyph_")
if is_file(dst + "/en_US/en_US_thes.dat"):
delete_file(dst + "/en_US/en_US_thes.dat")
delete_file(dst + "/en_US/en_US_thes.idx")
if is_file(dst + "/ru_RU/ru_RU_oo3.dic"):
delete_file(dst + "/ru_RU/ru_RU_oo3.dic")
delete_file(dst + "/ru_RU/ru_RU_oo3.aff")
@ -1775,6 +1947,38 @@ def check_module_version(actual_version, clear_func):
clear_func()
return
def set_sysroot_env(platform):
global ENV_BEFORE_SYSROOT
ENV_BEFORE_SYSROOT = dict(os.environ)
if "linux" != host_platform():
return
if config.option("sysroot") == "":
return
path = config.option("sysroot_" + platform)
sysroot_path_bin = config.get_custom_sysroot_bin(platform)
compiler_gcc_prefix = get_compiler_gcc_prefix(platform)
os.environ['PATH'] = sysroot_path_bin + ":" + get_env("PATH")
os.environ['LD_LIBRARY_PATH'] = config.get_custom_sysroot_lib(platform)
os.environ['CC'] = compiler_gcc_prefix + "gcc"
os.environ['CXX'] = compiler_gcc_prefix + "g++"
os.environ['AR'] = compiler_gcc_prefix + "ar"
os.environ['RANLIB'] = compiler_gcc_prefix + "ranlib"
os.environ['CFLAGS'] = "--sysroot=" + path
os.environ['CXXFLAGS'] = "--sysroot=" + path
os.environ['LDFLAGS'] = "--sysroot=" + path
check_python()
return
def restore_sysroot_env():
os.environ.clear()
os.environ.update(ENV_BEFORE_SYSROOT)
def check_python():
if ("linux" != host_platform()):
return
@ -1782,8 +1986,10 @@ def check_python():
directory_bin = __file__script__path__ + "/../tools/linux/python3/bin"
if not is_dir(directory + "/python3"):
cmd("tar", ["xfz", directory + "/python3.tar.gz", "-C", directory])
cmd("ln", ["-s", directory_bin + "/python3", directory_bin + "/python"])
download('https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/python/python3.tar.gz', directory + "/python3.tar.gz")
download('https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/python/extract.sh', directory + "/extract.sh")
cmd_in_dir(directory, "chmod", ["+x", "./extract.sh"])
cmd_in_dir(directory, "./extract.sh")
directory_bin = directory_bin.replace(" ", "\\ ")
os.environ["PATH"] = directory_bin + os.pathsep + os.environ["PATH"]
return
@ -1812,9 +2018,11 @@ def get_autobuild_version(product, platform="", branch="", build=""):
download_platform = platform
if ("" == download_platform):
osType = get_platform()
isArm = True if (-1 != osType.find("arm")) else False
is64 = True if (osType.endswith("64")) else False
isArm = False
if (-1 != osType.find("arm")) or (-1 != osType.find("aarch64")):
isArm = True
if ("windows" == host_platform()):
download_platform = "win-"
elif ("linux" == host_platform()):
@ -1838,9 +2046,42 @@ def get_autobuild_version(product, platform="", branch="", build=""):
download_addon = download_branch + "/" + download_build + "/" + product + "-" + download_platform + ".7z"
return "http://repo-doc-onlyoffice-com.s3.amazonaws.com/archive/" + download_addon
def create_x2t_js_cache(dir, product, platform):
if is_file(dir + "/libdoctrenderer.dylib") and (os.path.getsize(dir + "/libdoctrenderer.dylib") < 5*1024*1024):
def is_use_create_artifacts_qemu_any_platform():
if config.check_option("platform", "win_arm64") and not is_os_arm():
return True
return False
def is_use_create_artifacts_qemu(platform):
if platform == "win_arm64" and not is_os_arm():
return True
return False
def create_artifacts_qemu_any_platform():
if not is_use_create_artifacts_qemu_any_platform():
return
if config.check_option("platform", "win_arm64"):
create_artifacts_qemu_win_arm()
return;
def create_artifacts_qemu_win_arm():
if config.option("qemu-win-arm64-dir") == "":
print("For deploying win_arm64 on non arm host you should provide qemu-win-arm64-dir. More info in tools/win/qemu/README.md")
return
old_curr_dir = os.path.abspath(os.curdir)
qemu_dir = os.path.abspath(config.option("qemu-win-arm64-dir"))
os.chdir(qemu_dir)
start_qemu_bat_path = f"start.bat"
cmd(start_qemu_bat_path, [])
os.chdir(old_curr_dir)
def create_x2t_js_cache(dir, product, platform):
# mac
if is_file(dir + "/libdoctrenderer.dylib") or is_dir(dir + "/doctrenderer.framework"):
doctrenderer_lib = "libdoctrenderer.dylib" if is_file(dir + "/libdoctrenderer.dylib") else "doctrenderer.framework/doctrenderer"
if os.path.getsize(dir + "/" + doctrenderer_lib) < 5*1024*1024:
return
if ((platform == "linux_arm64") and not is_os_arm()):
cmd_in_dir_qemu(platform, dir, "./x2t", ["-create-js-snapshots"], True)
@ -1851,6 +2092,35 @@ def create_x2t_js_cache(dir, product, platform):
def setup_local_qmake(dir_qmake):
dir_base = os.path.dirname(dir_qmake)
writeFile(dir_base + "/onlyoffice_qt.conf", "Prefix = " + dir_base)
writeFile(dir_base + "/onlyoffice_qt.conf", "Prefix = " + dir_base)
return
def deploy_icu(core_dir, dst_dir, platform):
if (0 == platform.find("android")):
src_dir = core_dir + "/Common/3dParty/icu/android/build/" + platform[8:]
copy_file(src_dir + "/icudt" + icu_ver + "l.dat", dst_dir + "/icudt" + icu_ver + "l.dat")
return
isXp = False
if platform.endswith("xp"):
isXp = True
platform = platform[0:-3]
src_dir = core_dir + "/Common/3dParty/icu/" + platform + "/build"
if (0 == platform.find("win")):
icu_ver_win = icu_ver
if isXp:
icu_ver_win = icu_ver_old
src_dir += "/xp"
copy_file(src_dir + "/icudt" + icu_ver_win + ".dll", dst_dir + "/icudt" + icu_ver_win + ".dll")
copy_file(src_dir + "/icuuc" + icu_ver_win + ".dll", dst_dir + "/icuuc" + icu_ver_win + ".dll")
if (0 == platform.find("linux")):
copy_file(src_dir + "/libicudata.so." + icu_ver, dst_dir + "/libicudata.so." + icu_ver)
copy_file(src_dir + "/libicuuc.so." + icu_ver, dst_dir + "/libicuuc.so." + icu_ver)
if (0 == platform.find("mac") and not config.check_option("config", "bundle_dylibs")):
copy_file(src_dir + "/libicudata." + icu_ver + ".dylib", dst_dir + "/libicudata." + icu_ver + ".dylib")
copy_file(src_dir + "/libicuuc." + icu_ver + ".dylib", dst_dir + "/libicuuc." + icu_ver + ".dylib")
return

View File

@ -33,24 +33,29 @@ def make():
base.set_env('NODE_ENV', 'production')
base_dir = base.get_script_dir() + "/.."
out_dir = base_dir + "/out/js/";
out_dir = base_dir + "/out/js/"
branding = config.option("branding-name")
if ("" == branding):
branding = "onlyoffice"
out_dir += branding
base.create_dir(out_dir)
isOnlyMobile = False
if (config.option("module") == "mobile"):
isOnlyMobile = True
# builder
base.cmd_in_dir(base_dir + "/../web-apps/translation", "python", ["merge_and_check.py"])
build_interface(base_dir + "/../web-apps/build")
build_sdk_builder(base_dir + "/../sdkjs/build")
base.create_dir(out_dir + "/builder")
base.copy_dir(base_dir + "/../web-apps/deploy/web-apps", out_dir + "/builder/web-apps")
base.copy_dir(base_dir + "/../sdkjs/deploy/sdkjs", out_dir + "/builder/sdkjs")
correct_sdkjs_licence(out_dir + "/builder/sdkjs")
if not isOnlyMobile:
base.cmd_in_dir(base_dir + "/../web-apps/translation", "python", ["merge_and_check.py"])
build_interface(base_dir + "/../web-apps/build")
build_sdk_builder(base_dir + "/../sdkjs/build")
base.create_dir(out_dir + "/builder")
base.copy_dir(base_dir + "/../web-apps/deploy/web-apps", out_dir + "/builder/web-apps")
base.copy_dir(base_dir + "/../sdkjs/deploy/sdkjs", out_dir + "/builder/sdkjs")
correct_sdkjs_licence(out_dir + "/builder/sdkjs")
# desktop
if config.check_option("module", "desktop"):
if config.check_option("module", "desktop") and not isOnlyMobile:
build_sdk_desktop(base_dir + "/../sdkjs/build")
base.create_dir(out_dir + "/desktop")
base.copy_dir(base_dir + "/../sdkjs/deploy/sdkjs", out_dir + "/desktop/sdkjs")
@ -60,10 +65,6 @@ def make():
deldirs = ['ie', 'mobile', 'embed']
[base.delete_dir(root + "/" + d) for root, dirs, f in os.walk(out_dir + "/desktop/web-apps/apps") for d in dirs if d in deldirs]
# for bug 62528. remove empty folders
walklist = list(os.walk(out_dir + "/desktop/sdkjs"))
[os.remove(p) for p, _, _ in walklist[::-1] if len(os.listdir(p)) == 0]
base.copy_file(base_dir + "/../web-apps/apps/api/documents/index.html.desktop", out_dir + "/desktop/web-apps/apps/api/documents/index.html")
build_interface(base_dir + "/../desktop-apps/common/loginpage/build")
@ -72,7 +73,7 @@ def make():
# mobile
if config.check_option("module", "mobile"):
build_sdk_native(base_dir + "/../sdkjs/build", False)
build_sdk_native(base_dir + "/../sdkjs/build")
base.create_dir(out_dir + "/mobile")
base.create_dir(out_dir + "/mobile/sdkjs")
vendor_dir_src = base_dir + "/../web-apps/vendor/"
@ -104,7 +105,10 @@ def make():
# JS build
def _run_npm(directory):
return base.cmd_in_dir(directory, "npm", ["install"])
retValue = base.cmd_in_dir(directory, "npm", ["install"], True)
if (0 != retValue):
retValue = base.cmd_in_dir(directory, "npm", ["install", "--verbose"])
return retValue
def _run_npm_ci(directory):
return base.cmd_in_dir(directory, "npm", ["ci"])
@ -117,7 +121,7 @@ def _run_grunt(directory, params=[]):
def build_interface(directory):
_run_npm(directory)
_run_grunt(directory, ["--force"] + base.web_apps_addons_param())
_run_grunt(directory, ["--force", "--verbose"] + base.web_apps_addons_param())
return
def get_build_param(minimize=True):

View File

@ -11,6 +11,7 @@ def make():
git_dir = base.get_script_dir() + "/../.."
server_dir = base.get_script_dir() + "/../../server"
server_admin_panel_dir = base.get_script_dir() + "/../../server-admin-panel"
branding_dir = server_dir + "/branding"
if("" != config.option("branding")):
@ -49,9 +50,11 @@ def make():
if ("windows" == base.host_platform()):
pkg_target += "-win"
base.cmd_in_dir(server_dir + "/DocService", "pkg", [".", "-t", pkg_target, "--options", "max_old_space_size=4096", "-o", "docservice"])
base.cmd_in_dir(server_dir + "/DocService", "pkg", [".", "-t", pkg_target, "--options", "max_old_space_size=6144", "-o", "docservice"])
base.cmd_in_dir(server_dir + "/FileConverter", "pkg", [".", "-t", pkg_target, "-o", "converter"])
base.cmd_in_dir(server_dir + "/Metrics", "pkg", [".", "-t", pkg_target, "-o", "metrics"])
if "server-admin-panel" in base.get_server_addons() and base.is_exist(server_admin_panel_dir):
base.cmd_in_dir(server_admin_panel_dir + "/server", "pkg", [".", "-t", pkg_target, "-o", "adminpanel"])
example_dir = base.get_script_dir() + "/../../document-server-integration/web/documentserver-example/nodejs"
base.delete_dir(example_dir + "/node_modules")
@ -65,8 +68,10 @@ def build_server_with_addons():
for addon in addons:
if (addon):
addon_dir = base.get_script_dir() + "/../../" + addon
if (base.is_exist(addon_dir)):
if (base.is_exist(addon_dir + "/package.json")):
base.print_info("npm ci: " + addon)
base.cmd_in_dir(addon_dir, "npm", ["ci"])
base.print_info("npm run build: " + addon)
base.cmd_in_dir(addon_dir, "npm", ["run", "build"])
def build_server_develop():

View File

@ -24,7 +24,7 @@ def parse():
# all platforms
global platforms
platforms = ["win_64", "win_32", "win_64_xp", "win_32_xp",
platforms = ["win_64", "win_32", "win_64_xp", "win_32_xp", "win_arm64",
"linux_64", "linux_32", "linux_arm64",
"mac_64", "mac_arm64",
"ios",
@ -57,11 +57,11 @@ def parse():
if not check_option("platform", "mac_64"):
options["platform"] = "mac_64 " + options["platform"]
if ("linux" == host_platform) and check_option("platform", "linux_arm64") and not base.is_os_arm():
if not check_option("platform", "linux_64"):
# linux_64 binaries need only for desktop
if check_option("module", "desktop"):
options["platform"] = "linux_64 " + options["platform"]
if (False):
# use qemu on deploy for emulation
if ("windows" == host_platform) and check_option("platform", "win_arm64") and not base.is_os_arm():
if not check_option("platform", "win_64"):
options["platform"] = "win_64 " + options["platform"]
if check_option("platform", "xp") and ("windows" == host_platform):
options["platform"] += " win_64_xp win_32_xp"
@ -77,6 +77,27 @@ def parse():
if ("windows" == host_platform) and ("2019" == option("vs-version")):
extend_option("config", "vs2019")
# sysroot setup
if "linux" != host_platform and "sysroot" in options:
options["sysroot"] = ""
if "linux" == host_platform and "sysroot" in options:
if options["sysroot"] == "0":
options["sysroot"] = ""
elif options["sysroot"] == "1":
dst_dir = os.path.abspath(base.get_script_dir(__file__) + '/../tools/linux/sysroot')
dst_dir_amd64 = dst_dir + "/ubuntu16-amd64-sysroot"
dst_dir_arm64 = dst_dir + "/ubuntu16-arm64-sysroot"
if not base.is_dir(dst_dir_amd64) or not base.is_dir(dst_dir_arm64):
base.cmd_in_dir(dst_dir, "python3", ["./fetch.py", "all"])
options["sysroot_linux_64"] = dst_dir_amd64
options["sysroot_linux_arm64"] = dst_dir_arm64
else:
# specific sysroot => one platform for build!
options["sysroot"] = "1"
options["sysroot_linux_64"] = options["sysroot"]
options["sysroot_linux_arm64"] = options["sysroot"]
if is_cef_107():
extend_option("config", "cef_version_107")
@ -104,9 +125,6 @@ def parse():
if not "sdkjs-plugin-server" in options:
options["sdkjs-plugin-server"] = "default"
if not "arm64-toolchain-bin" in options:
options["arm64-toolchain-bin"] = "/usr/bin"
if check_option("platform", "ios"):
if not check_option("config", "no_bundle_xcframeworks"):
if not check_option("config", "bundle_xcframeworks"):
@ -116,6 +134,10 @@ def parse():
if not check_option("config", "bundle_dylibs"):
extend_option("config", "bundle_dylibs")
if ("mac" == host_platform) and check_option("module", "desktop"):
if not check_option("config", "bundle_dylibs"):
extend_option("config", "bundle_dylibs")
if check_option("use-system-qt", "1"):
base.cmd_in_dir(base.get_script_dir() + "/../tools/linux", "python", ["use_system_qt.py"])
options["qt-dir"] = base.get_script_dir() + "/../tools/linux/system_qt"
@ -139,6 +161,9 @@ def check_compiler(platform):
if (0 == platform.find("win")):
compiler["compiler"] = "msvc" + options["vs-version"]
compiler["compiler_64"] = "msvc" + options["vs-version"] + "_64"
if (0 == platform.find("win_arm")):
compiler["compiler"] = "msvc" + options["vs-version"] + "_arm"
compiler["compiler_64"] = "msvc" + options["vs-version"] + "_arm64"
elif (0 == platform.find("linux")):
compiler["compiler"] = "gcc"
compiler["compiler_64"] = "gcc_64"
@ -199,6 +224,26 @@ def is_mobile_platform():
return True
return False
def get_custom_sysroot_bin(platform):
use_platform = platform
if "linux_arm64" == platform and not base.is_os_arm():
# use cross compiler
use_platform = "linux_64"
return option("sysroot_" + use_platform) + "/usr/bin"
def get_custom_sysroot_lib(platform, isNatural=False):
use_platform = platform
if "linux_arm64" == platform and not base.is_os_arm() and not isNatural:
# use cross compiler
use_platform = "linux_64"
if ("linux_64" == use_platform):
return option("sysroot_linux_64") + "/usr/lib/x86_64-linux-gnu"
if ("linux_arm64" == use_platform):
return option("sysroot_linux_arm64") + "/usr/lib/aarch64-linux-gnu"
return ""
def parse_defaults():
defaults_path = base.get_script_dir() + "/../defaults"
if ("" != option("branding")):

View File

@ -17,12 +17,15 @@ import websocket_all
import v8
import html2
import iwork
import md
import hunspell
import glew
import harfbuzz
import oo_brotli
import hyphen
import googletest
import libvlc
import heif
def check_android_ndk_macos_arm(dir):
if base.is_dir(dir + "/darwin-x86_64") and not base.is_dir(dir + "/darwin-arm64"):
@ -44,15 +47,18 @@ def make():
v8.make()
html2.make()
iwork.make(False)
md.make()
hunspell.make(False)
harfbuzz.make()
glew.make()
hyphen.make()
googletest.make()
oo_brotli.make()
heif.make()
if config.check_option("build-libvlc", "1"):
libvlc.make()
if config.check_option("module", "mobile"):
if (config.check_option("platform", "android")):
curl.make()

View File

@ -71,13 +71,6 @@ else:
"arch" : "darwin-x86_64"
}
def get_android_ndk_version():
#return "26.2.11394342"
return "21.1.6352462"
def get_android_ndk_version_major():
return int(get_android_ndk_version().split(".")[0])
def get_options_dict_as_array(opts):
value = []
for key in opts:

View File

@ -11,8 +11,8 @@ current_dir = os.path.abspath(current_dir)
if not current_dir.endswith("/"):
current_dir += "/"
icu_major = "58"
icu_minor = "3"
icu_major = "74"
icu_minor = "2"
options = {
"--enable-strict" : "no",
@ -41,12 +41,12 @@ cpp_flags_base = [
cpp_flags = [
"-fno-short-wchar",
"-fno-short-enums",
"-DU_USING_ICU_NAMESPACE=0",
"-DU_HAVE_NL_LANGINFO_CODESET=0",
"-DU_TIMEZONE=0",
"-DU_DISABLE_RENAMING=0",
"-DUCONFIG_NO_COLLATION=0",
"-DUCONFIG_NO_FORMATTING=0",
"-DUCONFIG_NO_REGULAR_EXPRESSIONS=0",
@ -55,16 +55,11 @@ cpp_flags = [
"-DU_STATIC_IMPLEMENTATION"
]
def fetch_icu():
def fetch_icu(major, minor):
if not base.is_dir(current_dir + "icu"):
base.cmd("git", ["clone", "--depth", "1", "--branch", "maint/maint-" + icu_major, "https://github.com/unicode-org/icu.git", current_dir + "icu2"])
base.cmd("git", ["clone", "--depth", "1", "--branch", "release-" + major + "-" + minor, "https://github.com/unicode-org/icu.git", current_dir + "icu2"])
base.copy_dir(current_dir + "icu2/icu4c", current_dir + "icu")
base.delete_dir_with_access_error(current_dir + "icu2")
if ("linux" == base.host_platform()):
base.replaceInFile(current_dir + "/icu/source/i18n/digitlst.cpp", "xlocale", "locale")
if False and ("mac" == base.host_platform()):
base.replaceInFile(current_dir + "/icu/source/tools/pkgdata/pkgdata.cpp", "cmd, \"%s %s -o %s%s %s %s%s %s %s\",", "cmd, \"%s %s -o %s%s %s %s %s %s %s\",")
return
def build_host():
@ -111,9 +106,9 @@ def build_arch(arch):
cross_build_dir = os.path.abspath(current_dir + "icu/cross_build")
arch_build_dir = os.path.abspath(current_dir + "build/tmp")
base.create_dir(arch_build_dir)
os.chdir(arch_build_dir)
base.cmd("./../../icu/source/configure", ["--with-cross-build=" + cross_build_dir] +
base.cmd("./../../icu/source/configure", ["--with-cross-build=" + cross_build_dir] +
android_ndk.get_options_dict_as_array(options) + ["--host=" + android_ndk.platforms[arch]["target"], "--prefix=" + arch_build_dir])
base.cmd("make", ["-j4"])
os.chdir(current_dir)
@ -132,7 +127,7 @@ def make():
old_env = dict(os.environ)
fetch_icu()
fetch_icu(icu_major, icu_minor)
build_host()

View File

@ -22,10 +22,10 @@ def move_debug_libs_windows(dir):
def clean():
if base.is_dir("boost_1_58_0"):
base.delete_dir_with_access_error("boost_1_58_0");
base.delete_dir_with_access_error("boost_1_58_0")
base.delete_dir("boost_1_58_0")
if base.is_dir("boost_1_72_0"):
base.delete_dir_with_access_error("boost_1_72_0");
base.delete_dir_with_access_error("boost_1_72_0")
base.delete_dir("boost_1_72_0")
if base.is_dir("build"):
base.delete_dir("build")
@ -85,14 +85,36 @@ def make():
base.cmd("b2.exe", ["headers"])
base.cmd("b2.exe", ["--clean"])
base.cmd("b2.exe", ["--prefix=./../build/win_32", "link=static", "--with-filesystem", "--with-system", "--with-date_time", "--with-regex", "--toolset=" + win_toolset, "address-model=32", "install"])
if (-1 != config.option("platform").find("win_arm64") and not base.is_file("../build/win_arm64/lib/libboost_system-" + win_vs_version + "-mt-a64-1_72.lib")):
boost_bat = []
boost_bat.append("call bootstrap.bat " + win_boot_arg) # first build b2 for win64, so vcvarsall_call with arm64 later
vcvarsall_call = ("call \"" + config.option("vs-path") + "/vcvarsall.bat\" " + "x64_arm64")
boost_bat.append(vcvarsall_call)
boost_bat.append("call b2.exe headers")
boost_bat.append("call b2.exe --clean")
boost_bat.append("call b2.exe --prefix=./../build/win_arm64 architecture=arm link=static --with-filesystem --with-system --with-date_time --with-regex --toolset=" + win_toolset + " address-model=64 install")
base.run_as_bat(boost_bat)
correct_install_includes_win(base_dir, "win_64")
correct_install_includes_win(base_dir, "win_32")
correct_install_includes_win(base_dir, "win_32")
correct_install_includes_win(base_dir, "win_arm64")
if config.check_option("platform", "linux_64") and not base.is_dir("../build/linux_64"):
base.cmd("./bootstrap.sh", ["--with-libraries=filesystem,system,date_time,regex"])
base.cmd("./b2", ["headers"])
base.cmd("./b2", ["--clean"])
base.cmd("./b2", ["--prefix=./../build/linux_64", "link=static", "cxxflags=-fPIC", "install"])
if config.option("sysroot") == "":
addon_config = []
addon_compile = []
if "1" == config.option("use-clang"):
addon_config = ["--with-toolset=clang"]
addon_compile = ["cxxflags=-stdlib=libc++", "linkflags=-stdlib=libc++", "define=_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION"]
base.cmd("./bootstrap.sh", ["--with-libraries=filesystem,system,date_time,regex"] + addon_config)
base.cmd("./b2", ["headers"])
base.cmd("./b2", ["--clean"])
base.cmd("./b2", ["--prefix=./../build/linux_64", "link=static", "cxxflags=-fPIC"] + addon_compile + ["install"])
else: # build via qmake when custom sysroot is needed
boost_qt.make(os.getcwd(), ["filesystem", "system", "date_time", "regex"], "linux_64")
directory_build = base_dir + "/build/linux_64/lib"
base.delete_file(directory_build + "/libboost_system.a")
base.delete_file(directory_build + "/libboost_system.dylib")
base.copy_files(directory_build + "/linux_64/*.a", directory_build)
# TODO: support x86
if config.check_option("platform", "linux_arm64") and not base.is_dir("../build/linux_arm64"):
@ -116,14 +138,14 @@ def make():
if (-1 != config.option("platform").find("android")) and not base.is_dir("../build/android"):
boost_qt.make(os.getcwd(), ["filesystem", "system", "date_time", "regex"])
if (-1 != config.option("platform").find("mac")) and not base.is_dir("../build/mac_64"):
if config.check_option("platform", "mac_64") and not base.is_dir("../build/mac_64"):
boost_qt.make(os.getcwd(), ["filesystem", "system", "date_time", "regex"], "mac_64")
directory_build = base_dir + "/build/mac_64/lib"
base.delete_file(directory_build + "/libboost_system.a")
base.delete_file(directory_build + "/libboost_system.dylib")
base.copy_files(directory_build + "/mac_64/*.a", directory_build)
if (-1 != config.option("platform").find("mac_arm64")) and not base.is_dir("../build/mac_arm64"):
if config.check_option("platform", "mac_arm64") and not base.is_dir("../build/mac_arm64"):
boost_qt.make(os.getcwd(), ["filesystem", "system", "date_time", "regex"], "mac_arm64")
directory_build = base_dir + "/build/mac_arm64/lib"
base.delete_file(directory_build + "/libboost_system.a")

View File

@ -9,11 +9,14 @@ import qmake
def make(src_dir, modules, build_platform="android", qmake_addon=""):
old_cur = os.getcwd()
old_env = dict(os.environ)
b2_addon = ""
print("boost-headers...")
base.cmd("./bootstrap.sh", ["--with-libraries=system"])
base.cmd("./bootstrap.sh", ["--with-libraries=system"])
base.cmd("./b2", ["--prefix=./../build/" + build_platform, "headers", "install"])
for module in modules:
print("boost-module: " + module + " ...")
module_dir = src_dir + "/libs/" + module
@ -40,6 +43,8 @@ def make(src_dir, modules, build_platform="android", qmake_addon=""):
base.save_as_script(module_dir + "/" + module + ".pro", pro_file_content)
os.chdir(module_dir)
qmake.make_all_platforms(module_dir + "/" + module + ".pro", qmake_addon)
os.environ.clear()
os.environ.update(old_env)
os.chdir(old_cur)
return

View File

@ -5,6 +5,13 @@ sys.path.append('../..')
import config
import base
import os
import glob
def clear_module():
for child in glob.glob("./*"):
if base.is_dir(child):
base.delete_dir(child)
return
def make():
print("[fetch & build]: cef")
@ -13,13 +20,14 @@ def make():
old_cur = os.getcwd()
os.chdir(base_dir)
platforms = ["win_64", "win_32", "win_64_xp", "win_32_xp", "linux_64", "linux_32", "mac_64", "mac_arm64"]
base.check_module_version("2", clear_module)
platforms = ["win_64", "win_32", "win_64_xp", "win_32_xp", "linux_64", "linux_32", "mac_64", "mac_arm64", "win_arm64", "linux_arm64"]
for platform in platforms:
if not config.check_option("platform", platform):
continue
url = "http://d2ettrnqo7v976.cloudfront.net/cef/"
url = "https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/cef/"
archive_name = "./cef_binary.7z"
if (-1 != platform.find("_xp")):

View File

@ -8,7 +8,7 @@ import os
def clean():
if base.is_dir("glew-2.1.0"):
base.delete_dir("glew-2.1.0");
base.delete_dir("glew-2.1.0")
return
def make():
@ -16,7 +16,7 @@ def make():
return
if not config.check_option("module", "mobile"):
return;
return
print("[fetch & build]: glew")
base_dir = base.get_script_dir() + "/../../core/Common/3dParty/glew"
@ -26,7 +26,7 @@ def make():
base.common_check_version("glew", "1", clean)
if not base.is_dir("glew-2.1.0"):
base.download("https://deac-ams.dl.sourceforge.net/project/glew/glew/2.1.0/glew-2.1.0-win32.zip", "./archive.zip")
base.download("https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/glew/glew-2.1.0-win32.zip", "./archive.zip")
base.extract("./archive.zip", "./")
base.delete_file("./archive.zip")

View File

@ -0,0 +1,403 @@
import sys
sys.path.append('../..')
import base
import os
import config
# NOTE:
# - requires CMake >= 3.21, < 4.0.0
# libs versions
X265_VERSION = "4.1"
DE265_VERSION = "1.0.16"
# 1.18.2 - the latest version of libheif supporting C++11 builds (as for now)
HEIF_VERSION = "1.18.2"
# ios cmake toolchain
IOS_CMAKE_VERSION = "4.5.0"
IOS_CMAKE_TOOLCHAIN_FILE = base.get_script_dir() + "/../../core/Common/3dParty/heif/ios-cmake/ios.toolchain.cmake"
# android cmake toolchain
ANDROID_CMAKE_TOOLCHAIN_FILE = base.get_env("ANDROID_NDK_ROOT") + "/build/cmake/android.toolchain.cmake"
# linux arm64 cmake toolchain
LINUX_SYSTEM_AARCH64_TOOLCHAIN_FILE = base.get_script_dir() + "/../tools/linux/sysroot/system-aarch64.toolchain.cmake"
LINUX_CUSTOM_SYSROOT_TOOLCHAIN_FILE = base.get_script_dir() + "/../tools/linux/sysroot/custom-sysroot.toolchain.cmake"
OLD_ENV = dict()
def get_vs_version():
vs_version = "14 2015"
if config.option("vs-version") == "2019":
vs_version = "16 2019"
return vs_version
def get_xcode_sdk(platform):
xcode_sdk = "iphoneos"
if "simulator" in platform:
xcode_sdk = "iphonesimulator"
return xcode_sdk
def fetch_repo(repo_url, branch_or_tag):
base.cmd("git", ["clone", "--depth", "1", "--branch", branch_or_tag, repo_url])
return
def get_build_dir(base_dir, repo_dir, platform, build_type):
return os.path.join(base_dir, repo_dir, "build", platform, build_type.lower())
# general build function that builds for ONE platform (supposing we are located in the build directory)
def build_with_cmake(platform, cmake_args, build_type):
# extend cmake arguments
cmake_args_ext = []
# WINDOWS
if "win" in platform:
cmake_args_ext = [
"-G", f"Visual Studio {get_vs_version()}"
]
if platform == "win_64" or platform == "win_64_xp":
cmake_args_ext += ["-A", "x64"]
elif platform == "win_32" or platform == "win_32_xp":
cmake_args_ext += ["-A", "Win32"]
elif platform == "win_arm64":
cmake_args_ext += ["-A", "ARM64"]
# LINUX, MAC
elif "linux" in platform or "mac" in platform:
cmake_args_ext = [
"-G", "Unix Makefiles",
"-DCMAKE_POSITION_INDEPENDENT_CODE=ON" # on UNIX we need to compile with fPIC
]
if platform == "mac_64":
cmake_args_ext += ["-DCMAKE_OSX_DEPLOYMENT_TARGET=10.11", "-DCMAKE_OSX_ARCHITECTURES=x86_64"]
elif platform == "mac_arm64":
cmake_args_ext += ["-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0", "-DCMAKE_OSX_ARCHITECTURES=arm64"]
elif config.option("sysroot") != "":
# force use custom CXXFLAGS with Release/Debug build
if (platform == "linux_64"):
cmake_args += ["-DCMAKE_TOOLCHAIN_FILE=" + LINUX_CUSTOM_SYSROOT_TOOLCHAIN_FILE]
else:
cmake_args += ["-DCMAKE_TOOLCHAIN_FILE=" + LINUX_SYSTEM_AARCH64_TOOLCHAIN_FILE]
elif platform == "linux_arm64" and not base.is_os_arm():
cmake_args += ["-DCMAKE_TOOLCHAIN_FILE=" + LINUX_SYSTEM_AARCH64_TOOLCHAIN_FILE]
# IOS
elif "ios" in platform:
cmake_args_ext = [
"-G", "Xcode",
"-DCMAKE_TOOLCHAIN_FILE=" + IOS_CMAKE_TOOLCHAIN_FILE,
"-DDEPLOYMENT_TARGET=11.0"
]
if platform == "ios":
cmake_args_ext += ["-DPLATFORM=OS64"]
elif platform == "ios_simulator":
cmake_args_ext += ["-DPLATFORM=SIMULATOR64COMBINED"]
# ANDROID
elif "android" in platform:
cmake_args_ext = [
"-G", "Unix Makefiles",
"-DCMAKE_TOOLCHAIN_FILE=" + ANDROID_CMAKE_TOOLCHAIN_FILE,
"-DCMAKE_POSITION_INDEPENDENT_CODE=ON"
]
def get_cmake_args_android(arch, api_level):
return [
"-DANDROID_ABI=" + arch,
"-DANDROID_NATIVE_API_LEVEL=" + api_level
]
if platform == "android_arm64_v8a":
cmake_args_ext += get_cmake_args_android("arm64-v8a", "21")
elif platform == "android_armv7":
cmake_args_ext += get_cmake_args_android("armeabi-v7a", "16")
elif platform == "android_x86":
cmake_args_ext += get_cmake_args_android("x86", "16")
elif platform == "android_x86_64":
cmake_args_ext += get_cmake_args_android("x86_64", "21")
# env setup for custom sysroot
if config.option("sysroot") != "":
base.set_sysroot_env(platform)
# run cmake
base.cmd("cmake", cmake_args + cmake_args_ext)
# build
if "Unix Makefiles" in cmake_args_ext:
base.cmd("make", ["-j4"])
else:
base.cmd("cmake", ["--build", ".", "--config", build_type])
if config.option("sysroot") != "":
base.restore_sysroot_env()
return
# general make function that calls `build_func` callback for configured platform(s) with specified cmake arguments
def make_common(build_func, cmake_args):
# WINDOWS
if "windows" == base.host_platform():
# win_64
if config.check_option("platform", "win_64") or config.check_option("platform", "win_64_xp"):
build_func("win_64", cmake_args)
# win_32
if config.check_option("platform", "win_32") or config.check_option("platform", "win_32_xp"):
build_func("win_32", cmake_args)
# win_arm64
if config.check_option("platform", "win_arm64"):
build_func("win_arm64", cmake_args)
# LINUX
elif "linux" == base.host_platform():
# linux_64
if config.check_option("platform", "linux_64"):
build_func("linux_64", cmake_args)
# linux_arm64
if config.check_option("platform", "linux_arm64"):
build_func("linux_arm64", cmake_args)
# MAC
elif "mac" == base.host_platform():
# mac_64
if config.check_option("platform", "mac_64"):
build_func("mac_64", cmake_args)
# mac_arm64
if config.check_option("platform", "mac_arm64"):
build_func("mac_arm64", cmake_args)
# IOS
if -1 != config.option("platform").find("ios"):
# ios (arm64)
build_func("ios", cmake_args)
# ios simulator (x86_64 and arm64 FAT lib)
build_func("ios_simulator", cmake_args)
# ANDROID
if -1 != config.option("platform").find("android"):
# android_arm64_v8a
if config.check_option("platform", "android_arm64_v8a"):
build_func("android_arm64_v8a", cmake_args)
# android_armv7
if config.check_option("platform", "android_armv7"):
build_func("android_armv7", cmake_args)
# android_x86
if config.check_option("platform", "android_x86"):
build_func("android_x86", cmake_args)
# android_x86_64
if config.check_option("platform", "android_x86_64"):
build_func("android_x86_64", cmake_args)
return
def make_x265(base_dir, build_type):
# fetch lib repo
if not base.is_dir("x265_git"):
fetch_repo("https://bitbucket.org/multicoreware/x265_git.git", f"Release_{X265_VERSION}")
# fix x265 version detection so it reads version from x265Version.txt instead of parsing it from .git
base.replaceInFile(
base_dir + "/x265_git/source/cmake/Version.cmake",
"elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../x265Version.txt)",
"endif()\n if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../x265Version.txt)"
)
# prepare cmake args
cmake_dir = base_dir + "/x265_git/source"
cmake_args = [
cmake_dir,
"-DCMAKE_BUILD_TYPE=" + build_type,
"-DENABLE_CLI=OFF", # do not build standalone CLI app
"-DENABLE_SHARED=OFF", # do not build shared libs
"-DENABLE_ASSEMBLY=OFF", # disable assembly optimizations
"-DENABLE_LIBNUMA=OFF", # disable libnuma usage (affects Linux only)
]
# lib build function
def build_x265(platform, cmake_args):
# check if target lib has already been built
build_dir = get_build_dir(base_dir, "x265_git", platform, build_type)
if platform.find("win") != -1:
target_lib = os.path.join(build_dir, build_type, "x265-static.lib")
else:
target_lib = os.path.join(build_dir, "libx265.a")
if base.is_file(target_lib):
return
# go to the build directory
base.create_dir(build_dir)
os.chdir(build_dir)
# run build
build_with_cmake(platform, cmake_args, build_type)
# for iOS there is no target for building libx265.a, so we need to form it ourselves from libcommon.a and libencoder.a
if platform.find("ios") != -1:
xcode_sdk = get_xcode_sdk(platform)
base.cmd("libtool", [
"-static",
"-o", "libx265.a",
f"build/common.build/{build_type}-{xcode_sdk}/libcommon.a",
f"build/encoder.build/{build_type}-{xcode_sdk}/libencoder.a"
])
# copy header
base.copy_file(base_dir + "/x265_git/source/x265.h", build_dir)
# reset directory
os.chdir(base_dir)
return
make_common(build_x265, cmake_args)
return
def make_de265(base_dir, build_type):
# fetch lib repo
if not base.is_dir("libde265"):
fetch_repo("https://github.com/strukturag/libde265.git", f"v{DE265_VERSION}")
# prepare cmake args
cmake_dir = base_dir + "/libde265"
cmake_args = [
cmake_dir,
"-DCMAKE_BUILD_TYPE=" + build_type,
"-DBUILD_SHARED_LIBS=OFF", # do not build shared libs
"-DENABLE_SDL=OFF", # disable SDL
"-DENABLE_DECODER=OFF", # do not build decoder CLI executable
"-DENABLE_ENCODER=OFF", # do not build encoder CLI executable
]
# lib build function
def build_de265(platform, cmake_args):
# check if target lib has already been built
build_dir = get_build_dir(base_dir, "libde265", platform, build_type)
if platform.find("win") != -1:
target_lib = os.path.join(build_dir, "libde265", build_type, "libde265.lib")
else:
target_lib = os.path.join(build_dir, "libde265/libde265.a")
if base.is_file(target_lib):
return
# go to the build directory
base.create_dir(build_dir)
os.chdir(build_dir)
# run build
build_with_cmake(platform, cmake_args, build_type)
# for ios copy target library from the default build path
if platform.find("ios") != -1:
xcode_sdk = get_xcode_sdk(platform)
base.copy_file(f"libde265/{build_type}-{xcode_sdk}/libde265.a", "libde265")
# copy header
base.copy_file(base_dir + "/libde265/libde265/de265.h", "libde265")
# reset directory
os.chdir(base_dir)
return
make_common(build_de265, cmake_args)
return
def make_heif(base_dir, build_type):
# fetch lib repo
if not base.is_dir("libheif"):
fetch_repo("https://github.com/strukturag/libheif.git", f"v{HEIF_VERSION}")
# do not build heifio module
base.replaceInFile(
base_dir + "/libheif/CMakeLists.txt",
"add_subdirectory(heifio)",
"# add_subdirectory(heifio)"
)
base.replaceInFile(
base_dir + "/libheif/CMakeLists.txt",
"if (DOXYGEN_FOUND)",
"if (FALSE)"
)
# prepare cmake args
cmake_dir = base_dir + "/libheif"
cmake_args = [
cmake_dir,
"--preset=release-noplugins", # preset to disable plugins system
"-DCMAKE_BUILD_TYPE=" + build_type,
"-DBUILD_SHARED_LIBS=OFF", # do not build shared libs
"-DWITH_LIBSHARPYUV=OFF", # do not build libsharpyuv (for RGB <--> YUV color space conversions)
"-DWITH_AOM_DECODER=OFF", # do not build AOM V1 decoder (for AVIF image format)
"-DWITH_AOM_ENCODER=OFF", # do not build AOM V1 encoder (for AVIF image format)
"-DWITH_GDK_PIXBUF=OFF", # do not build gdk-pixbuf plugin (UNIX only)
"-DWITH_GNOME=OFF", # do not build gnome plugin (Linux only)
"-DWITH_EXAMPLES=OFF", # do not build examples
"-DWITH_EXAMPLE_HEIF_VIEW=OFF", # do not build heif-view CLI tool
"-DWITH_X265=ON", # enable x265 codec
"-DWITH_LIBDE265=ON", # enable de265 codec
"-DCMAKE_CXX_FLAGS=-DLIBDE265_STATIC_BUILD", # add macro definition to properly compile with de265 static library
"-DCMAKE_C_FLAGS=-DLIBDE265_STATIC_BUILD", # same ^
]
# lib build function
def build_heif(platform, cmake_args):
# check if target lib has already been built
build_dir = get_build_dir(base_dir, "libheif", platform, build_type)
if platform.find("win") != -1:
target_lib = os.path.join(build_dir, "libheif", build_type, "heif.lib")
else:
target_lib = os.path.join(build_dir, "libheif/libheif.a")
if base.is_file(target_lib):
return
# go to the build directory
base.create_dir(build_dir)
os.chdir(build_dir)
# add paths to dependent libraries and includes to cmake args
de265_build_dir = get_build_dir(base_dir, "libde265", platform, build_type)
x265_build_dir = get_build_dir(base_dir, "x265_git", platform, build_type)
cmake_args_ext = [
f"-DLIBDE265_INCLUDE_DIR={de265_build_dir}",
f"-DX265_INCLUDE_DIR={x265_build_dir}"
]
if platform.find("win") != -1:
cmake_args_ext += [
f"-DLIBDE265_LIBRARY={de265_build_dir}/libde265/{build_type}/libde265.lib",
f"-DX265_LIBRARY={x265_build_dir}/{build_type}/x265-static.lib"
]
else:
cmake_args_ext += [
f"-DLIBDE265_LIBRARY={de265_build_dir}/libde265/libde265.a",
f"-DX265_LIBRARY={x265_build_dir}/libx265.a"
]
# run build
build_with_cmake(platform, cmake_args + cmake_args_ext, build_type)
# for ios copy target library from the default build path
if platform.find("ios") != -1:
xcode_sdk = get_xcode_sdk(platform)
base.copy_file(f"libheif/{build_type}-{xcode_sdk}/libheif.a", "libheif")
# reset directory
os.chdir(base_dir)
return
make_common(build_heif, cmake_args)
return
def clear_module():
if base.is_dir("libde265"):
base.delete_dir_with_access_error("libde265")
if base.is_dir("x265_git"):
base.delete_dir_with_access_error("x265_git")
if base.is_dir("libheif"):
base.delete_dir_with_access_error("libheif")
return
def make():
print("[fetch & build]: heif")
base_dir = base.get_script_dir() + "/../../core/Common/3dParty/heif"
old_dir = os.getcwd()
os.chdir(base_dir)
base.check_module_version("2", clear_module)
build_type = "Release"
if (-1 != config.option("config").lower().find("debug")):
build_type = "Debug"
# fetch custom cmake toolchain for ios
if -1 != config.option("platform").find("ios"):
if not base.is_file(IOS_CMAKE_TOOLCHAIN_FILE):
fetch_repo("https://github.com/leetal/ios-cmake.git", IOS_CMAKE_VERSION)
# build encoder library
make_x265(base_dir, build_type)
# build decoder library
make_de265(base_dir, build_type)
# build libheif
make_heif(base_dir, build_type)
os.chdir(old_dir)
return
if __name__ == '__main__':
make()

View File

@ -9,13 +9,12 @@ import os
import glob
import icu_android
def fetch_icu(major, minor):
def fetch_icu(major, minor, target_dir="icu"):
if (base.is_dir("./icu2")):
base.delete_dir_with_access_error("icu2")
base.cmd("git", ["clone", "--depth", "1", "--branch", "maint/maint-" + major, "https://github.com/unicode-org/icu.git", "./icu2"])
base.copy_dir("./icu2/icu4c", "./icu")
base.delete_dir_with_access_error("icu2")
base.cmd("git", ["clone", "--depth", "1", "--branch", "release-" + major + "-" + minor, "https://github.com/unicode-org/icu.git", "./icu2"])
base.copy_dir("./icu2/icu4c", target_dir)
base.delete_dir_with_access_error("icu2")
#base.cmd("svn", ["export", "https://github.com/unicode-org/icu/tags/release-" + icu_major + "-" + icu_minor + "/icu4c", "./icu", "--non-interactive", "--trust-server-cert"])
return
def clear_module():
@ -36,18 +35,26 @@ def make():
old_cur = os.getcwd()
os.chdir(base_dir)
base.check_module_version("3", clear_module)
base.check_module_version("8", clear_module)
if (-1 != config.option("platform").find("android")):
icu_android.make()
os.chdir(base_dir)
icu_major = "58"
icu_minor = "3"
icu_major = "74"
icu_minor = "2"
if not base.is_dir("icu"):
fetch_icu(icu_major, icu_minor)
fetch_icu(icu_major, icu_minor)
# old version for win_xp
icu_major_old = "58"
icu_minor_old = "3"
if config.check_option("platform", "win_64_xp") or config.check_option("platform", "win_32_xp"):
if not base.is_dir("icu58"):
fetch_icu(icu_major_old, icu_minor_old, "icu58")
if ("windows" == base.host_platform()):
platformToolset = "v140"
@ -58,55 +65,109 @@ def make():
need_platforms.append("win_64")
if (-1 != config.option("platform").find("win_32")):
need_platforms.append("win_32")
if (-1 != config.option("platform").find("win_arm64")):
need_platforms.append("win_64") # for exe files
need_platforms.append("win_arm64")
def build_icu_win(source_dir, out_dir, icu_major):
if base.is_dir(out_dir):
return
compile_bat = []
compile_bat.append("setlocal")
args = {
"win_32" : {
"msbuild_platfrom" : "Win32",
"vcvarsall_arch" : "x86",
"out_bin_dir" : source_dir + "/bin/",
"out_lib_dir" : source_dir + "/lib/"
},
"win_64" : {
"msbuild_platfrom" : "X64",
"vcvarsall_arch" : "x64",
"out_bin_dir" : source_dir + "/bin64/",
"out_lib_dir" : source_dir + "/lib64/"
},
"win_arm64" : {
"msbuild_platfrom" : "ARM64",
"vcvarsall_arch" : "x64_arm64",
"out_bin_dir" : source_dir + "/binARM64/",
"out_lib_dir" : source_dir + "/libARM64/"
}
}
platform_args = args[platform]
compile_bat.append("call \"" + config.option("vs-path") + "/vcvarsall.bat\" " + platform_args['vcvarsall_arch'])
compile_bat.append("call MSBuild.exe " + source_dir + "/source/allinone/allinone.sln /p:Configuration=Release /p:PlatformToolset=" + platformToolset + " /p:Platform=" + platform_args['msbuild_platfrom'])
compile_bat.append("endlocal")
base.run_as_bat(compile_bat)
base.create_dir(out_dir)
base.copy_file(platform_args['out_bin_dir'] + "icudt" + icu_major + ".dll", out_dir)
base.copy_file(platform_args['out_bin_dir'] + "icuuc" + icu_major + ".dll", out_dir)
base.copy_file(platform_args['out_lib_dir'] + "icudt.lib", out_dir)
base.copy_file(platform_args['out_lib_dir'] + "icuuc.lib", out_dir)
for platform in need_platforms:
if not config.check_option("platform", platform) and not config.check_option("platform", platform + "_xp"):
continue
if not base.is_dir(platform + "/build"):
base.create_dir(platform)
compile_bat = []
compile_bat.append("setlocal")
compile_bat.append("call \"" + config.option("vs-path") + "/vcvarsall.bat\" " + ("x86" if base.platform_is_32(platform) else "x64"))
compile_bat.append("call MSBuild.exe icu/source/allinone/allinone.sln /p:Configuration=Release /p:PlatformToolset=" + platformToolset + " /p:Platform=" + ("Win32" if base.platform_is_32(platform) else "X64"))
compile_bat.append("endlocal")
base.run_as_bat(compile_bat)
bin_dir = "icu/bin64/" if ("win_64" == platform) else "icu/bin/"
lib_dir = "icu/lib64/" if ("win_64" == platform) else "icu/lib/"
base.create_dir(platform + "/build")
base.copy_file(bin_dir + "icudt" + icu_major + ".dll", platform + "/build/")
base.copy_file(bin_dir + "icuuc" + icu_major + ".dll", platform + "/build/")
base.copy_file(lib_dir + "icudt.lib", platform + "/build/")
base.copy_file(lib_dir + "icuuc.lib", platform + "/build/")
if not (config.check_option("platform", "win_64_xp") or config.check_option("platform", "win_32_xp")):
build_icu_win("icu", platform + "/build", icu_major)
else:
# xp
build_icu_win("icu58", platform + "/build/xp", icu_major_old)
os.chdir(old_cur)
return
if ("linux" == base.host_platform()):
if not base.is_file("./icu/source/i18n/digitlst.cpp.bak"):
base.copy_file("./icu/source/i18n/digitlst.cpp", "./icu/source/i18n/digitlst.cpp.bak")
base.replaceInFile("./icu/source/i18n/digitlst.cpp", "xlocale", "locale")
if base.is_dir(base_dir + "/linux_64"):
base.delete_dir(base_dir + "/linux_64")
if base.is_dir(base_dir + "/linux_arm64"):
base.delete_dir(base_dir + "/linux_arm64")
if not base.is_dir(base_dir + "/linux_64"):
base.create_dir(base_dir + "/icu/cross_build")
os.chdir("icu/cross_build")
base.cmd("./../source/runConfigureICU", ["Linux", "--prefix=" + base_dir + "/icu/cross_build_install"])
base.replaceInFile("./../source/icudefs.mk.in", "LDFLAGS = @LDFLAGS@ $(RPATHLDFLAGS)", "LDFLAGS = @LDFLAGS@ $(RPATHLDFLAGS) -static-libstdc++ -static-libgcc")
base.cmd("make", ["-j4"])
base.cmd("make", ["install"], True)
command_configure = "./../source/runConfigureICU"
command_compile_addon = "-static-libstdc++ -static-libgcc"
if "1" == config.option("use-clang"):
command_configure = "CXXFLAGS=-stdlib=libc++ " + command_configure
command_compile_addon = "-stdlib=libc++"
if "" == config.option("sysroot"):
base.cmd(command_configure, ["Linux", "--prefix=" + base_dir + "/icu/cross_build_install"])
base.replaceInFile("./../source/icudefs.mk.in", "LDFLAGS = @LDFLAGS@ $(RPATHLDFLAGS)", "LDFLAGS = @LDFLAGS@ $(RPATHLDFLAGS) " + command_compile_addon)
else:
base.set_sysroot_env("linux_64")
sysroot_path = config.option("sysroot_linux_64")
sysroot_path_bin = config.get_custom_sysroot_bin("linux_64")
base.cmd_exe("./../source/configure", ["--prefix=" + base_dir + "/icu/cross_build_install",
"CC=" + sysroot_path_bin + "/gcc", "CXX=" + sysroot_path_bin + "/g++",
"AR=" + sysroot_path_bin + "/ar", "RANLIB=" + sysroot_path_bin + "/ranlib",
"CFLAGS=--sysroot=" + sysroot_path,
"CXXFLAGS=--sysroot=" + sysroot_path + " " + command_compile_addon,
"LDFLAGS=--sysroot=" + sysroot_path])
if "" == config.option("sysroot"):
base.cmd("make", ["-j4"])
base.cmd("make", ["install"], True)
else:
base.cmd_exe("make", ["-j4"])
base.cmd_exe("make", ["install"], True)
base.restore_sysroot_env()
base.create_dir(base_dir + "/linux_64")
base.create_dir(base_dir + "/linux_64/build")
base.copy_file(base_dir + "/icu/cross_build_install/lib/libicudata.so." + icu_major + "." + icu_minor, base_dir + "/linux_64/build/libicudata.so." + icu_major)
base.copy_file(base_dir + "/icu/cross_build_install/lib/libicuuc.so." + icu_major + "." + icu_minor, base_dir + "/linux_64/build/libicuuc.so." + icu_major)
base.copy_dir(base_dir + "/icu/cross_build_install/include", base_dir + "/linux_64/build/include")
if config.check_option("platform", "linux_arm64") and not base.is_dir(base_dir + "/linux_arm64") and not base.is_os_arm():
base.create_dir(base_dir + "/icu/linux_arm64")
os.chdir(base_dir + "/icu/linux_arm64")
base_arm_tool_dir = base.get_prefix_cross_compiler_arm64()
compiler_gcc_prefix = base.get_compiler_gcc_prefix("linux_arm64")
if config.option("sysroot") != "":
base.set_sysroot_env("linux_arm64")
base.cmd("./../source/configure", ["--host=arm-linux", "--prefix=" + base_dir + "/icu/linux_arm64_install", "--with-cross-build=" + base_dir + "/icu/cross_build",
"CC=" + base_arm_tool_dir + "gcc", "CXX=" + base_arm_tool_dir + "g++", "AR=" + base_arm_tool_dir + "ar", "RANLIB=" + base_arm_tool_dir + "ranlib"])
"CC=" + compiler_gcc_prefix + "gcc", "CXX=" + compiler_gcc_prefix + "g++", "AR=" + compiler_gcc_prefix + "ar", "RANLIB=" + compiler_gcc_prefix + "ranlib"])
base.cmd("make", ["-j4"])
base.cmd("make", ["install"], True)
base.create_dir(base_dir + "/linux_arm64")
@ -114,6 +175,8 @@ def make():
base.copy_file(base_dir + "/icu/linux_arm64_install/lib/libicudata.so." + icu_major + "." + icu_minor, base_dir + "/linux_arm64/build/libicudata.so." + icu_major)
base.copy_file(base_dir + "/icu/linux_arm64_install/lib/libicuuc.so." + icu_major + "." + icu_minor, base_dir + "/linux_arm64/build/libicuuc.so." + icu_major)
base.copy_dir(base_dir + "/icu/linux_arm64_install/include", base_dir + "/linux_arm64/build/include")
if config.option("sysroot") != "":
base.restore_sysroot_env()
os.chdir("../..")
@ -130,6 +193,6 @@ def make():
if (-1 != config.option("platform").find("ios")):
if not base.is_dir("build"):
base.bash("./icu_ios")
os.chdir(old_cur)
return

View File

@ -5,37 +5,29 @@ sys.path.append('../..')
import base
import os
def change_icu_defs(current_dir, arch):
icudef_file = current_dir + "/icudefs.mk"
icudef_file_old = current_dir + "/icudefs.mk.back"
def change_icu_defs(arch):
old_env = dict(os.environ)
param = "-arch x86_64"
if arch == "arm64":
param = "-arch arm64 -isysroot " + base.find_mac_sdk()
param = "-arch arm64"
param += " -isysroot " + base.find_mac_sdk()
param += " -mmacosx-version-min=10.12"
base.copy_file(icudef_file, icudef_file_old)
os.environ["CFLAGS"] = param
os.environ["CXXFLAGS"] = param + " --std=c++11"
os.environ["LDFLAGS"] = param
base.replaceInFile(icudef_file, "CFLAGS = ", "CFLAGS = " + param + " ")
base.replaceInFile(icudef_file, "CXXFLAGS = ", "CXXFLAGS = " + param + " ")
base.replaceInFile(icudef_file, "RPATHLDFLAGS =", "RPATHLDFLAGS2 =")
base.replaceInFile(icudef_file, "LDFLAGS = ", "LDFLAGS = " + param + " ")
base.replaceInFile(icudef_file, "RPATHLDFLAGS2 =", "RPATHLDFLAGS =")
return old_env
def restore_icu_defs(old_env):
os.environ.clear()
os.environ.update(old_env)
return
def restore_icu_defs(current_dir):
icudef_file = current_dir + "/icudefs.mk"
icudef_file_old = current_dir + "/icudefs.mk.back"
base.delete_file(icudef_file)
base.copy_file(icudef_file_old, icudef_file)
base.delete_file(icudef_file_old)
return
icu_major = "58"
icu_minor = "3"
icu_major = "74"
icu_minor = "2"
current_dir_old = os.getcwd()
current_dir = base.get_script_dir() + "/../../core/Common/3dParty/icu"
@ -46,29 +38,33 @@ if not base.is_dir(current_dir + "/mac_cross_64"):
base.create_dir(current_dir + "/mac_cross_64")
os.chdir(current_dir + "/mac_cross_64")
base.cmd("../icu/source/runConfigureICU", ["MacOSX",
"--prefix=" + current_dir + "/mac_cross_64", "CFLAGS=-Os CXXFLAGS=--std=c++11"])
old_env = change_icu_defs("x86_64")
change_icu_defs(current_dir + "/mac_cross_64", "x86_64")
base.cmd("../icu/source/runConfigureICU", ["MacOSX",
"--prefix=" + current_dir + "/mac_64_install", "--enable-static"])
base.cmd("make", ["-j4"])
base.cmd("make", ["install"], True)
restore_icu_defs(current_dir + "/mac_cross_64")
restore_icu_defs(old_env)
os.chdir(current_dir)
os.chdir(current_dir + "/icu/source")
base.cmd("./configure", ["--prefix=" + current_dir + "/mac_arm_64",
"--with-cross-build=" + current_dir + "/mac_cross_64", "VERBOSE=1"])
old_env = change_icu_defs("arm64")
change_icu_defs(current_dir + "/icu/source", "arm64")
addon = []
if not base.is_os_arm():
addon = ["--host=aarch64-apple-darwin"]
base.cmd("./configure", ["--prefix=" + current_dir + "/mac_arm64_install",
"--with-cross-build=" + current_dir + "/mac_cross_64", "--enable-static", "VERBOSE=1"] + addon)
base.cmd("make", ["-j4"])
base.cmd("make", ["install"])
restore_icu_defs(current_dir + "/icu/source")
restore_icu_defs(old_env)
os.chdir(current_dir)
@ -84,15 +80,26 @@ base.create_dir(current_dir + "/mac_64/build")
base.create_dir(current_dir + "/mac_arm64")
base.create_dir(current_dir + "/mac_arm64/build")
base.copy_dir(current_dir + "/mac_cross_64/include", current_dir + "/mac_64/build/include")
base.copy_file(current_dir + "/mac_cross_64/lib/libicudata." + icu_major + "." + icu_minor + ".dylib", current_dir + "/mac_64/build/libicudata." + icu_major + ".dylib")
base.copy_file(current_dir + "/mac_cross_64/lib/libicuuc." + icu_major + "." + icu_minor + ".dylib", current_dir + "/mac_64/build/libicuuc." + icu_major + ".dylib")
base.copy_dir(current_dir + "/mac_64_install/include", current_dir + "/mac_64/build/include")
# copy shared libs
base.copy_file(current_dir + "/mac_64_install/lib/libicudata." + icu_major + "." + icu_minor + ".dylib", current_dir + "/mac_64/build/libicudata." + icu_major + ".dylib")
base.copy_file(current_dir + "/mac_64_install/lib/libicuuc." + icu_major + "." + icu_minor + ".dylib", current_dir + "/mac_64/build/libicuuc." + icu_major + ".dylib")
# copy static libs
base.copy_file(current_dir + "/mac_64_install/lib/libicudata.a", current_dir + "/mac_64/build")
base.copy_file(current_dir + "/mac_64_install/lib/libicui18n.a", current_dir + "/mac_64/build")
base.copy_file(current_dir + "/mac_64_install/lib/libicuuc.a", current_dir + "/mac_64/build")
base.copy_dir(current_dir + "/mac_arm_64/include", current_dir + "/mac_arm64/build/include")
base.copy_file(current_dir + "/mac_arm_64/lib/libicudata." + icu_major + "." + icu_minor + ".dylib", current_dir + "/mac_arm64/build/libicudata." + icu_major + ".dylib")
base.copy_file(current_dir + "/mac_arm_64/lib/libicuuc." + icu_major + "." + icu_minor + ".dylib", current_dir + "/mac_arm64/build/libicuuc." + icu_major + ".dylib")
base.copy_dir(current_dir + "/mac_arm64_install/include", current_dir + "/mac_arm64/build/include")
# copy shared libs
base.copy_file(current_dir + "/mac_arm64_install/lib/libicudata." + icu_major + "." + icu_minor + ".dylib", current_dir + "/mac_arm64/build/libicudata." + icu_major + ".dylib")
base.copy_file(current_dir + "/mac_arm64_install/lib/libicuuc." + icu_major + "." + icu_minor + ".dylib", current_dir + "/mac_arm64/build/libicuuc." + icu_major + ".dylib")
# copy static libs
base.copy_file(current_dir + "/mac_arm64_install/lib/libicudata.a", current_dir + "/mac_arm64/build")
base.copy_file(current_dir + "/mac_arm64_install/lib/libicui18n.a", current_dir + "/mac_arm64/build")
base.copy_file(current_dir + "/mac_arm64_install/lib/libicuuc.a", current_dir + "/mac_arm64/build")
base.delete_dir(current_dir + "/mac_cross_64")
base.delete_dir(current_dir + "/mac_arm_64")
base.delete_dir(current_dir + "/mac_64_install")
base.delete_dir(current_dir + "/mac_arm64_install")
os.chdir(current_dir_old)

View File

@ -22,7 +22,7 @@ def make(use_gperf = True):
base_dir = base.get_script_dir() + "/../../core/Common/3dParty/apple"
os.chdir(base_dir)
base.check_module_version("3", clear_module)
base.check_module_version("4", clear_module)
os.chdir(old_cur_dir)
cmd_args = ["fetch.py"]
@ -35,4 +35,5 @@ def make(use_gperf = True):
if __name__ == '__main__':
# manual compile
make(False)
make(False)

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python
import sys
sys.path.append('../..')
import config
import base
import os
import subprocess
def make():
print("[fetch]: md")
base_dir = base.get_script_dir() + "/../../core/Common/3dParty/md"
base.cmd_in_dir(base_dir, "python", ["fetch.py"])
return
if __name__ == '__main__':
# manual compile
make()

View File

@ -0,0 +1,15 @@
#!/usr/bin/env python
import sys
sys.path.append('../..')
import base
import os
def make():
print("[fetch & build]: brotli")
base.cmd_in_dir(base.get_script_dir() + "/../../core/Common/3dParty/brotli", "./make.py")
return
if __name__ == '__main__':
# manual compile
make()

View File

@ -54,6 +54,14 @@ def make():
qmake_bat.append("call nmake clean")
qmake_bat.append("call nmake build_libs install")
base.run_as_bat(qmake_bat, True)
if (-1 != config.option("platform").find("win_arm64")) and not base.is_dir("../build/win_arm64"):
base.create_dir("./../build/win_arm64")
qmake_bat = []
qmake_bat.append("call \"" + config.option("vs-path") + "/vcvarsall.bat\" x64_arm64")
qmake_bat.append("perl Configure VC-WIN64-ARM --prefix=" + old_cur_dir + "\\build\\win_arm64 --openssldir=" + old_cur_dir + "\\build\\win_arm64 no-shared no-asm enable-md2")
qmake_bat.append("call nmake clean")
qmake_bat.append("call nmake build_libs install")
base.run_as_bat(qmake_bat, True)
os.chdir(old_cur)
# xp ----------------------------------------------------------------------------------------------------
os.chdir(base_dir + "/openssl")
@ -79,28 +87,47 @@ def make():
# -------------------------------------------------------------------------------------------------------
return
if (-1 != config.option("platform").find("linux")) and not base.is_dir("../build/linux_64"):
if (-1 != config.option("platform").find("linux")) and not base.is_dir("../build/linux_64"):
base.cmd("./config", ["enable-md2", "no-shared", "no-asm", "--prefix=" + old_cur_dir + "/build/linux_64", "--openssldir=" + old_cur_dir + "/build/linux_64"])
base.replaceInFile("./Makefile", "CFLAGS=-Wall -O3", "CFLAGS=-Wall -O3 -fvisibility=hidden")
base.replaceInFile("./Makefile", "CXXFLAGS=-Wall -O3", "CXXFLAGS=-Wall -O3 -fvisibility=hidden")
base.cmd("make")
base.cmd("make", ["install"])
base.cmd("make", ["clean"], True)
# TODO: support x86
if "1" == config.option("use-clang"):
base.replaceInFile("./Makefile", "CC=$(CROSS_COMPILE)gcc", "CC=$(CROSS_COMPILE)clang")
base.replaceInFile("./Makefile", "CXX=$(CROSS_COMPILE)g++", "CXX=$(CROSS_COMPILE)clang++")
base.replaceInFile("./Makefile", "CFLAGS=-Wall -O3", "CFLAGS=-Wall -O3 -fvisibility=hidden")
base.replaceInFile("./Makefile", "CXXFLAGS=-Wall -O3", "CXXFLAGS=-Wall -O3 -fvisibility=hidden -stdlib=libc++")
base.replaceInFile("./Makefile", "LDFLAGS=", "LDFLAGS=-stdlib=libc++")
elif config.option("sysroot") == "":
base.replaceInFile("./Makefile", "CFLAGS=-Wall -O3", "CFLAGS=-Wall -O3 -fvisibility=hidden")
base.replaceInFile("./Makefile", "CXXFLAGS=-Wall -O3", "CXXFLAGS=-Wall -O3 -fvisibility=hidden")
else:
base.replaceInFile("./Makefile", "CROSS_COMPILE=", "CROSS_COMPILE=" + config.get_custom_sysroot_bin("linux_64") + "/")
base.replaceInFile("./Makefile", "CFLAGS=-Wall -O3", "CFLAGS=-Wall -O3 -fvisibility=hidden --sysroot=" + config.option("sysroot_linux_64"))
base.replaceInFile("./Makefile", "CXXFLAGS=-Wall -O3", "CXXFLAGS=-Wall -O3 -fvisibility=hidden --sysroot=" + config.option("sysroot_linux_64"))
if config.option("sysroot") == "":
base.cmd("make", [])
base.cmd("make", ["install"])
base.cmd("make", ["clean"], True)
else:
base.set_sysroot_env("linux_64")
base.cmd_exe("make", [])
base.cmd_exe("make", ["install"])
base.cmd_exe("make", ["clean"], True)
base.restore_sysroot_env()
if (-1 != config.option("platform").find("linux_arm64")) and not base.is_dir("../build/linux_arm64"):
if ("x86_64" != platform.machine()):
if (base.is_os_arm()):
base.copy_dir("../build/linux_64", "../build/linux_arm64")
else:
cross_compiler_arm64 = config.option("arm64-toolchain-bin")
if ("" == cross_compiler_arm64):
cross_compiler_arm64 = "/usr/bin"
cross_compiler_arm64_prefix = cross_compiler_arm64 + "/" + base.get_prefix_cross_compiler_arm64()
base.cmd("./Configure", ["linux-aarch64", "--cross-compile-prefix=" + cross_compiler_arm64_prefix, "enable-md2", "no-shared", "no-asm", "no-tests", "--prefix=" + old_cur_dir + "/build/linux_arm64", "--openssldir=" + old_cur_dir + "/build/linux_arm64"])
if config.option("sysroot") != "":
base.set_sysroot_env("linux_arm64")
base.cmd("/usr/bin/perl", ["./Configure", "linux-aarch64", "enable-md2", "no-shared", "no-asm", "no-tests", "--prefix=" + old_cur_dir + "/build/linux_arm64", "--openssldir=" + old_cur_dir + "/build/linux_arm64"])
#base.cmd("./Configure", ["linux-aarch64", "enable-md2", "no-shared", "no-asm", "no-tests", "--prefix=" + old_cur_dir + "/build/linux_arm64", "--openssldir=" + old_cur_dir + "/build/linux_arm64"])
base.replaceInFile("./Makefile", "CFLAGS=-Wall -O3", "CFLAGS=-Wall -O3 -fvisibility=hidden")
base.replaceInFile("./Makefile", "CXXFLAGS=-Wall -O3", "CXXFLAGS=-Wall -O3 -fvisibility=hidden")
base.cmd("make", [], True)
base.cmd("make", ["install"], True)
if config.option("sysroot") != "":
base.restore_sysroot_env()
if (-1 != config.option("platform").find("mac")) and not base.is_dir("../build/mac_64"):
base.cmd("./Configure", ["enable-md2", "no-shared", "no-asm", "darwin64-x86_64-cc", "--prefix=" + old_cur_dir + "/build/mac_64", "--openssldir=" + old_cur_dir + "/build/mac_64", "-mmacosx-version-min=10.11"])

View File

@ -8,6 +8,11 @@ import os
import subprocess
import glob
def clean():
if base.is_dir("socket.io-client-cpp"):
base.delete_dir_with_access_error("socket.io-client-cpp")
return
def correct_namespace(dir):
folder = dir
if ("/" != folder[-1:]):
@ -25,6 +30,12 @@ def correct_namespace(dir):
def make():
base_dir = base.get_script_dir() + "/../../core/Common/3dParty/socketio"
old_cur = os.getcwd()
os.chdir(base_dir)
base.common_check_version("socketio", "2", clean)
os.chdir(old_cur)
if not base.is_dir(base_dir + "/socket.io-client-cpp"):
base.cmd_in_dir(base_dir, "git", ["clone", "https://github.com/socketio/socket.io-client-cpp.git"])
base.cmd_in_dir(base_dir + "/socket.io-client-cpp", "git", ["checkout", "da779141a7379cc30c870d48295033bc16a23c66"])
@ -37,6 +48,7 @@ def make():
base.apply_patch(base_dir + "/socket.io-client-cpp/src/internal/sio_client_impl.cpp", base_dir + "/patches/sio_client_impl_fail.patch")
base.apply_patch(base_dir + "/socket.io-client-cpp/src/internal/sio_client_impl.cpp", base_dir + "/patches/sio_client_impl_open.patch")
base.apply_patch(base_dir + "/socket.io-client-cpp/src/internal/sio_client_impl.cpp", base_dir + "/patches/sio_client_impl_close_timeout.patch")
base.apply_patch(base_dir + "/socket.io-client-cpp/src/internal/sio_client_impl.cpp", base_dir + "/patches/sio_client_impl_encode.patch")
# no tls realization (remove if socket.io fix this)
dst_dir = base_dir + "/socket.io-client-cpp/src_no_tls"

View File

@ -10,10 +10,10 @@ import v8_89
def clean():
if base.is_dir("depot_tools"):
base.delete_dir_with_access_error("depot_tools");
base.delete_dir_with_access_error("depot_tools")
base.delete_dir("depot_tools")
if base.is_dir("v8"):
base.delete_dir_with_access_error("v8");
base.delete_dir_with_access_error("v8")
base.delete_dir("v8")
if base.is_exist("./.gclient"):
base.delete_file("./.gclient")
@ -26,6 +26,8 @@ def clean():
def is_main_platform():
if (config.check_option("platform", "win_64") or config.check_option("platform", "win_32")):
return True
if (config.check_option("platform", "win_arm64")):
return True
if (config.check_option("platform", "linux_64") or config.check_option("platform", "linux_32") or config.check_option("platform", "linux_arm64")):
return True
if config.check_option("platform", "mac_64"):
@ -42,14 +44,9 @@ def is_xp_platform():
return False
def is_use_clang():
gcc_version = base.get_gcc_version()
is_clang = "false"
if (gcc_version >= 6000):
is_clang = "true"
print("gcc version: " + str(gcc_version) + ", use clang:" + is_clang)
return is_clang
if config.option("sysroot") == "" and "1" == config.option("use-clang"):
return "true"
return "false"
def make():
if not is_main_platform():
@ -233,8 +230,7 @@ def make_xp():
base.replaceInFile("depot_tools/cipd.ps1", "windows-386", "windows-amd64")
# old variant
#path_to_python2 = "/depot_tools/win_tools-2_7_13_chromium7_bin/python/bin"
path_to_python2 = "/depot_tools/bootstrap-2@3_8_10_chromium_23_bin/python/bin"
path_to_python2 = "/depot_tools/bootstrap-2@3_11_8_chromium_35_bin/python/bin"
os.environ["PATH"] = os.pathsep.join([base_dir + "/depot_tools",
base_dir + path_to_python2,
config.option("vs-path") + "/../Common7/IDE",
@ -269,7 +265,7 @@ def make_xp():
"for file in projects:",
" replaceInFile(file, '<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>', '<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>')",
" replaceInFile(file, '<RuntimeLibrary>MultiThreaded</RuntimeLibrary>', '<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>')",
]);
])
programFilesDir = base.get_env("ProgramFiles")
if ("" != base.get_env("ProgramFiles(x86)")):

View File

@ -7,6 +7,25 @@ import base
import os
import subprocess
def clean():
if base.is_dir("depot_tools"):
base.delete_dir_with_access_error("depot_tools")
base.delete_dir("depot_tools")
if base.is_dir("v8"):
base.delete_dir_with_access_error("v8")
base.delete_dir("v8")
if base.is_exist("./.gclient"):
base.delete_file("./.gclient")
if base.is_exist("./.gclient_entries"):
base.delete_file("./.gclient_entries")
if base.is_exist("./.gclient_previous_sync_commits"):
base.delete_file("./.gclient_previous_sync_commits")
if base.is_exist("./.gcs_entries"):
base.delete_file("./.gcs_entries")
if base.is_exist("./.cipd"):
base.delete_dir("./.cipd")
return
def change_bootstrap():
base.move_file("./depot_tools/bootstrap/manifest.txt", "./depot_tools/bootstrap/manifest.txt.bak")
content = "# changed by build_tools\n\n"
@ -16,7 +35,11 @@ def change_bootstrap():
content += "infra/3pp/tools/cpython/${platform} version:2@2.7.18.chromium.39\n\n"
content += "@Subdir python3\n"
content += "infra/3pp/tools/cpython3/${platform} version:2@3.8.10.chromium.23\n\n"
if ("windows" == base.host_platform()):
content += "infra/3pp/tools/cpython3/${platform} version:2@3.11.8.chromium.35\n\n"
else:
content += "infra/3pp/tools/cpython3/${platform} version:2@3.8.10.chromium.23\n\n"
content += "@Subdir git\n"
content += "infra/3pp/tools/git/${platform} version:2@2.41.0.chromium.11\n"
@ -30,6 +53,31 @@ def change_bootstrap():
base.writeFile("./depot_tools/bootstrap/manifest.txt", content)
return
def is_ubuntu_24_or_higher():
try:
with open('/etc/os-release') as f:
for line in f:
if line.startswith('VERSION_ID='):
version = line.split('=')[1].strip().strip('"')
return float(version) >= 24
except:
pass
return False
def fix_ubuntu24():
#if not is_ubuntu_24_or_higher():
# return
if "" == config.option("sysroot"):
return
old_cur = os.getcwd()
os.chdir("third_party/llvm-build/Release+Asserts/lib")
base.cmd("mv", ["libstdc++.so.6", "libstdc++.so.6.old"])
base.cmd("ln", ["-s", "/usr/lib/x86_64-linux-gnu/libstdc++.so.6", "libstdc++.so.6"])
os.chdir(old_cur)
return
def make_args(args, platform, is_64=True, is_debug=False):
args_copy = args[:]
if is_64:
@ -43,8 +91,13 @@ def make_args(args, platform, is_64=True, is_debug=False):
args_copy = args[:]
args_copy.append("target_cpu=\\\"arm64\\\"")
args_copy.append("v8_target_cpu=\\\"arm64\\\"")
args_copy.append("use_sysroot=true")
if (platform == "win_arm64"):
args_copy = args[:]
args_copy.append("target_cpu=\\\"arm64\\\"")
args_copy.append("v8_target_cpu=\\\"arm64\\\"")
args_copy.append("is_clang=false")
if is_debug:
args_copy.append("is_debug=true")
if (platform == "windows"):
@ -52,20 +105,63 @@ def make_args(args, platform, is_64=True, is_debug=False):
else:
args_copy.append("is_debug=false")
if (platform == "linux"):
args_copy.append("is_clang=true")
args_copy.append("use_sysroot=false")
linux_clang = False
if platform == "linux":
if "" != config.option("sysroot"):
args_copy.append("use_sysroot=true")
args_copy.append("is_clang=false")
if is_ubuntu_24_or_higher():
args_copy.append("use_gold=false")
args_copy.append("sysroot=\\\"" + config.option("sysroot_linux_64") + "\\\"")
args_copy.append("target_sysroot=\\\"" + config.option("sysroot_linux_64") + "\\\"")
else:
args_copy.append("is_clang=true")
if "1" == config.option("use-clang"):
linux_clang = True
else:
args_copy.append("use_sysroot=false")
if platform == "linux_arm64":
if "" != config.option("sysroot"):
args_copy.append("use_sysroot=true")
if is_ubuntu_24_or_higher():
args_copy.append("use_gold=false")
#args_copy.append("sysroot=\\\"" + config.option("sysroot_linux_64") + "\\\"")
args_copy.append("target_sysroot=\\\"" + config.option("sysroot_linux_arm64") + "\\\"")
else:
args_copy.append("is_clang=true")
if "1" == config.option("use-clang"):
linux_clang = True
else:
args_copy.append("use_sysroot=false")
if (platform == "windows"):
args_copy.append("is_clang=false")
if (platform == "mac") and base.is_os_arm():
args_copy.append("host_cpu=\\\"x64\\\"")
if linux_clang != True:
args_copy.append("use_custom_libcxx=false")
return "--args=\"" + " ".join(args_copy) + "\""
def ninja_windows_make(args, is_64=True, is_debug=False):
def ninja_windows_make(args, is_64=True, is_debug=False, is_arm=False):
directory_out = "out.gn/"
directory_out += ("win_64/" if is_64 else "win_32/")
if is_arm:
directory_out += "win_arm64/"
else:
directory_out += ("win_64/" if is_64 else "win_32/")
directory_out += ("debug" if is_debug else "release")
base.cmd2("gn", ["gen", directory_out, make_args(args, "windows", is_64, is_debug)])
if is_arm:
base.cmd2("gn", ["gen", directory_out, make_args(args, "win_arm64", is_64, is_debug)])
else:
base.cmd2("gn", ["gen", directory_out, make_args(args, "windows", is_64, is_debug)])
base.copy_file("./" + directory_out + "/obj/v8_wrappers.ninja", "./" + directory_out + "/obj/v8_wrappers.ninja.bak")
base.replaceInFile("./" + directory_out + "/obj/v8_wrappers.ninja", "target_output_name = v8_wrappers", "target_output_name = v8_wrappers\nbuild obj/v8_wrappers.obj: cxx ../../../src/base/platform/wrappers.cc")
base.replaceInFile("./" + directory_out + "/obj/v8_wrappers.ninja", "build obj/v8_wrappers.lib: alink", "build obj/v8_wrappers.lib: alink obj/v8_wrappers.obj")
@ -75,7 +171,10 @@ def ninja_windows_make(args, is_64=True, is_debug=False):
if (-1 == win_toolset_wrapper_file_content.find("line = line.decode('utf8')")):
base.replaceInFile(win_toolset_wrapper_file, "for line in link.stdout:\n", "for line in link.stdout:\n line = line.decode('utf8')\n")
base.cmd("ninja", ["-C", directory_out, "v8_wrappers"])
if is_arm:
base.copy_file('./' + directory_out + '/obj/v8_wrappers.lib', './' + directory_out + '/x64/obj/v8_wrappers.lib')
base.cmd("ninja", ["-C", directory_out])
base.delete_file("./" + directory_out + "/obj/v8_wrappers.ninja")
base.move_file("./" + directory_out + "/obj/v8_wrappers.ninja.bak", "./" + directory_out + "/obj/v8_wrappers.ninja")
@ -115,6 +214,8 @@ def make():
base.cmd("git", ["config", "--global", "http.postBuffer", "157286400"], True)
os.chdir(base_dir)
base.common_check_version("v8", "1", clean)
if not base.is_dir("depot_tools"):
base.cmd("git", ["clone", "https://chromium.googlesource.com/chromium/tools/depot_tools.git"])
change_bootstrap()
@ -138,11 +239,25 @@ def make():
base.cmd("./depot_tools/gclient", ["sync", "-r", v8_branch_version], True)
base.cmd("gclient", ["sync", "--force"], True)
base.copy_dir("./v8/third_party_new/ninja", "./v8/third_party/ninja")
if ("linux" == base.host_platform()):
if not base.is_file("./depot_tools/python3_bin_reldir.txt"):
base.cmd_in_dir("./depot_tools", "./ensure_bootstrap", [], True)
if ("windows" == base.host_platform()):
base.replaceInFile("v8/build/config/win/BUILD.gn", ":static_crt", ":dynamic_crt")
# fix for new depot_tools and vs2019, as VC folder contains a folder with a symbol in the name
# sorting is done by increasing version, so 0 is a dummy value
replace_src = " def to_int_if_int(x):\n try:\n return int(x)\n except ValueError:\n return x"
replace_dst = " def to_int_if_int(x):\n try:\n return int(x)\n except ValueError:\n return 0"
base.replaceInFile("v8/build/vs_toolchain.py", replace_src, replace_dst)
if not base.is_file("v8/src/base/platform/wrappers.cc"):
base.writeFile("v8/src/base/platform/wrappers.cc", "#include \"src/base/platform/wrappers.h\"\n")
if config.check_option("platform", "win_arm64"):
base.replaceInFile("v8/build/toolchain/win/setup_toolchain.py", "SDK_VERSION = \'10.0.26100.0\'", "SDK_VERSION = \'10.0.22621.0\'")
else:
base.replaceInFile("depot_tools/gclient_paths.py", "@functools.lru_cache", "")
@ -156,17 +271,35 @@ def make():
base.replaceInFile("v8/third_party/jinja2/tests.py", "from collections import Mapping", "try:\n from collections.abc import Mapping\nexcept ImportError:\n from collections import Mapping")
os.chdir("v8")
is_ubuntu24 = is_ubuntu_24_or_higher()
fix_ubuntu24()
gn_args = ["v8_static_library=true",
"is_component_build=false",
"v8_monolithic=true",
"v8_use_external_startup_data=false",
"use_custom_libcxx=false",
"treat_warnings_as_errors=false"]
if config.check_option("platform", "linux_64"):
base.cmd2("gn", ["gen", "out.gn/linux_64", make_args(gn_args, "linux")])
base.cmd("ninja", ["-C", "out.gn/linux_64"])
if config.option("sysroot") != "":
sysroot_path = config.option("sysroot_linux_64")
sysroot_path_bin = config.get_custom_sysroot_bin("linux_64")
old_env = dict(os.environ)
base.set_sysroot_env("linux_64")
pkg_old = os.environ.get("PKG_CONFIG_PATH", "")
os.environ["PKG_CONFIG_PATH"] = sysroot_path + "/usr/lib/x86_64-linux-gnu/pkgconfig:" + sysroot_path + "/usr/lib/pkgconfig:" + sysroot_path + "/usr/share/pkgconfig"
base.cmd2("gn", ["gen", "out.gn/linux_64", make_args(gn_args, "linux")], False)
os.environ["PKG_CONFIG_PATH"] = pkg_old
base.cmd2("ninja", ["-C", "out.gn/linux_64"], False)
base.restore_sysroot_env()
else:
base.cmd2("gn", ["gen", "out.gn/linux_64", make_args(gn_args, "linux")], False)
base.cmd2("ninja", ["-C", "out.gn/linux_64"], False)
if config.check_option("platform", "linux_32"):
base.cmd2("gn", ["gen", "out.gn/linux_32", make_args(gn_args, "linux", False)])
@ -174,12 +307,21 @@ def make():
if config.check_option("platform", "linux_arm64"):
base.cmd("build/linux/sysroot_scripts/install-sysroot.py", ["--arch=arm64"], False)
sysroot_path = config.option("sysroot_linux_64")
pkg_old = os.environ.get("PKG_CONFIG_PATH", "")
os.environ["PKG_CONFIG_PATH"] = sysroot_path + "/usr/lib/x86_64-linux-gnu/pkgconfig:" + sysroot_path + "/usr/lib/pkgconfig:" + sysroot_path + "/usr/share/pkgconfig"
base.cmd2("gn", ["gen", "out.gn/linux_arm64", make_args(gn_args, "linux_arm64", False)])
os.environ["PKG_CONFIG_PATH"] = pkg_old
base.cmd("ninja", ["-C", "out.gn/linux_arm64"])
if config.check_option("platform", "mac_64"):
base.cmd2("gn", ["gen", "out.gn/mac_64", make_args(gn_args, "mac")])
base.cmd("ninja", ["-C", "out.gn/mac_64"])
if config.check_option("platform", "win_arm64") and not base.is_file("out.gn/win_arm64/release/obj/v8_monolith.lib"):
ninja_windows_make(gn_args, True, False, True)
if config.check_option("platform", "win_64"):
if (-1 != config.option("config").lower().find("debug")):

View File

@ -22,4 +22,6 @@ def make():
deploy_mobile.make()
if config.check_option("module", "osign"):
deploy_osign.make()
if base.is_use_create_artifacts_qemu_any_platform():
base.create_artifacts_qemu_any_platform()
return

View File

@ -15,6 +15,7 @@ def make():
continue
root_dir = base_dir + ("/" + native_platform + "/" + branding + ("/DocumentBuilder" if base.is_windows() else "/documentbuilder"))
root_dir_win64 = base_dir + "/win_64/" + branding + "/DocumentBuilder"
if (base.is_dir(root_dir)):
base.delete_dir(root_dir)
base.create_dir(root_dir)
@ -39,12 +40,15 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "PdfFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "DjVuFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "XpsFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "OFDFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "HtmlFile2")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "Fb2File")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "EpubFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "IWorkFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "HWPFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "DocxRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "StarMathConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "ooxmlsignature", "xp" if isWindowsXP else "")
base.copy_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", root_dir + "/cmap.bin")
if ("ios" == platform):
@ -56,17 +60,7 @@ def make():
# base.generate_check_linux_system(git_dir + "/build_tools", root_dir)
# icu
if (0 == platform.find("win")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/icudt58.dll", root_dir + "/icudt58.dll")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/icuuc58.dll", root_dir + "/icuuc58.dll")
if (0 == platform.find("linux")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicudata.so.58", root_dir + "/libicudata.so.58")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicuuc.so.58", root_dir + "/libicuuc.so.58")
if (0 == platform.find("mac")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicudata.58.dylib", root_dir + "/libicudata.58.dylib")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicuuc.58.dylib", root_dir + "/libicuuc.58.dylib")
base.deploy_icu(core_dir, root_dir, native_platform)
# doctrenderer
if isWindowsXP:
@ -105,33 +99,41 @@ def make():
if (0 == platform.find("win")):
base.copy_file(core_dir + "/DesktopEditor/doctrenderer/docbuilder.com/src/docbuilder_midl.h", root_dir + "/include/docbuilder_midl.h")
base.replaceInFile(root_dir + "/include/docbuilder.h", "Q_DECL_EXPORT", "BUILDING_DOCBUILDER")
if ("win_64" == platform):
base.copy_file(core_dir + "/DesktopEditor/doctrenderer/docbuilder.com/deploy/win_64/docbuilder.com.dll", root_dir + "/docbuilder.com.dll")
base.copy_file(core_dir + "/DesktopEditor/doctrenderer/docbuilder.net/deploy/win_64/docbuilder.net.dll", root_dir + "/docbuilder.net.dll")
elif ("win_32" == platform):
base.copy_file(core_dir + "/DesktopEditor/doctrenderer/docbuilder.com/deploy/win_32/docbuilder.com.dll", root_dir + "/docbuilder.com.dll")
base.copy_file(core_dir + "/DesktopEditor/doctrenderer/docbuilder.net/deploy/win_32/docbuilder.net.dll", root_dir + "/docbuilder.net.dll")
# correct ios frameworks
if ("ios" == platform):
base.generate_plist(root_dir)
base.for_each_framework(root_dir, "ios", callbacks=[base.generate_plist, base.generate_xcprivacy])
if (0 == platform.find("linux")):
base.linux_correct_rpath_docbuilder(root_dir)
if (0 == platform.find("mac")):
base.for_each_framework(root_dir, "mac", callbacks=[base.generate_plist], max_depth=1)
base.mac_correct_rpath_x2t(root_dir)
base.mac_correct_rpath_docbuilder(root_dir)
base.create_x2t_js_cache(root_dir, "builder", platform)
base.create_dir(root_dir + "/fonts")
base.copy_dir(git_dir + "/core-fonts/asana", root_dir + "/fonts/asana")
base.copy_dir(git_dir + "/core-fonts/caladea", root_dir + "/fonts/caladea")
base.copy_dir(git_dir + "/core-fonts/crosextra", root_dir + "/fonts/crosextra")
base.copy_dir(git_dir + "/core-fonts/openoffice", root_dir + "/fonts/openoffice")
base.copy_file(git_dir + "/core-fonts/ASC.ttf", root_dir + "/fonts/ASC.ttf")
# delete unnecessary builder files
def delete_files(files):
for file in files:
base.delete_file(file)
delete_files(base.find_files(root_dir, "*.wasm"))
delete_files(base.find_files(root_dir, "*_ie.js"))
base.delete_file(root_dir + "/sdkjs/pdf/src/engine/cmap.bin")
@ -142,7 +144,6 @@ def make():
base.delete_dir(root_dir + "/sdkjs/cell/css")
base.delete_file(root_dir + "/sdkjs/pdf/src/engine/viewer.js")
base.delete_file(root_dir + "/sdkjs/common/spell/spell/spell.js.mem")
base.delete_dir(root_dir + "/sdkjs/common/Images")
base.delete_dir(root_dir + "/sdkjs/common/Images")
return

View File

@ -25,6 +25,7 @@ def make():
platform = native_platform
platform_postfix = platform + base.qt_dst_postfix()
isWindowsXP = False if (-1 == native_platform.find("_xp")) else True
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "kernel")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "kernel_network")
@ -32,6 +33,7 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "doctrenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "DjVuFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "XpsFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "OFDFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "PdfFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "HtmlFile2")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "UnicodeConverter")
@ -41,6 +43,8 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "HWPFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "DocxRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "hunspell")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "StarMathConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "ooxmlsignature", "xp" if isWindowsXP else "")
base.copy_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", archive_dir + "/cmap.bin")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "x2t")
@ -52,7 +56,8 @@ def make():
if ("windows" == base.host_platform()):
base.copy_files(core_dir + "/Common/3dParty/icu/" + platform + "/build/*.dll", archive_dir + "/")
else:
base.copy_files(core_dir + "/Common/3dParty/icu/" + platform + "/build/*", archive_dir + "/")
if not (0 == platform.find("mac") and config.check_option("config", "bundle_dylibs")):
base.copy_files(core_dir + "/Common/3dParty/icu/" + platform + "/build/*", archive_dir + "/")
base.copy_v8_files(core_dir, archive_dir, platform)
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "allfontsgen")
@ -65,6 +70,11 @@ def make():
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "metafiletester")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "dictionariestester")
# correct mac frameworks
if (0 == platform.find("mac")):
base.for_each_framework(archive_dir, "mac", callbacks=[base.generate_plist], max_depth=1)
base.mac_correct_rpath_x2t(archive_dir)
# js cache
base.generate_doctrenderer_config(archive_dir + "/DoctRenderer.config", "./", "builder", "", "./dictionaries")
base.create_x2t_js_cache(archive_dir, "core", platform)
@ -73,4 +83,3 @@ def make():
# dictionaries
base.copy_dictionaries(git_dir + "/dictionaries", archive_dir + "/dictionaries", True, False)
return

View File

@ -12,7 +12,7 @@ def copy_lib_with_links(src_dir, dst_dir, lib, version):
lib_major_name = lib + "." + major_version
base.copy_file(src_dir + "/" + lib_full_name, dst_dir + "/" + lib_full_name)
base.cmd_in_dir(dst_dir, "ln", ["-s", "./" + lib_full_name, "./" + lib_major_name])
base.cmd_in_dir(dst_dir, "ln", ["-s", "./" + lib_major_name, "./" + lib])
@ -40,7 +40,7 @@ def make():
isWindowsXP = False if (-1 == native_platform.find("_xp")) else True
platform = native_platform[0:-3] if isWindowsXP else native_platform
apps_postfix = "build" + base.qt_dst_postfix();
apps_postfix = "build" + base.qt_dst_postfix()
if ("" != config.option("branding")):
apps_postfix += ("/" + config.option("branding"))
apps_postfix += "/"
@ -65,13 +65,16 @@ def make():
base.copy_lib(build_libraries_path, root_dir + "/converter", "PdfFile")
base.copy_lib(build_libraries_path, root_dir + "/converter", "DjVuFile")
base.copy_lib(build_libraries_path, root_dir + "/converter", "XpsFile")
base.copy_lib(build_libraries_path, root_dir + "/converter", "OFDFile")
base.copy_lib(build_libraries_path, root_dir + "/converter", "HtmlFile2")
base.copy_lib(build_libraries_path, root_dir + "/converter", "Fb2File")
base.copy_lib(build_libraries_path, root_dir + "/converter", "EpubFile")
base.copy_lib(build_libraries_path, root_dir + "/converter", "IWorkFile")
base.copy_lib(build_libraries_path, root_dir + "/converter", "HWPFile")
base.copy_lib(build_libraries_path, root_dir + "/converter", "DocxRenderer")
base.copy_lib(build_libraries_path, root_dir + "/converter", "StarMathConverter")
base.copy_lib(build_libraries_path, root_dir + "/converter", "ooxmlsignature", "xp" if isWindowsXP else "")
if ("ios" == platform):
base.copy_lib(build_libraries_path, root_dir + "/converter", "x2t")
else:
@ -81,24 +84,13 @@ def make():
# base.generate_check_linux_system(git_dir + "/build_tools", root_dir + "/converter")
# icu
if (0 == platform.find("win")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/icudt58.dll", root_dir + "/converter/icudt58.dll")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/icuuc58.dll", root_dir + "/converter/icuuc58.dll")
#base.copy_file(git_dir + "/desktop-apps/common/converter/package.config", root_dir + "/converter/package.config")
if (0 == platform.find("linux")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicudata.so.58", root_dir + "/converter/libicudata.so.58")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicuuc.so.58", root_dir + "/converter/libicuuc.so.58")
if (0 == platform.find("mac")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicudata.58.dylib", root_dir + "/converter/libicudata.58.dylib")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicuuc.58.dylib", root_dir + "/converter/libicuuc.58.dylib")
base.deploy_icu(core_dir, root_dir + "/converter", native_platform)
# doctrenderer
if isWindowsXP:
base.copy_lib(build_libraries_path + "/xp", root_dir + "/converter", "doctrenderer")
else:
base.copy_lib(build_libraries_path, root_dir + "/converter", "doctrenderer")
base.copy_lib(build_libraries_path, root_dir + "/converter", "doctrenderer")
base.copy_v8_files(core_dir, root_dir + "/converter", platform, isWindowsXP)
base.generate_doctrenderer_config(root_dir + "/converter/DoctRenderer.config", "../editors/", "desktop", "", "../dictionaries")
@ -115,8 +107,6 @@ def make():
base.copy_dir(git_dir + "/core-fonts/openoffice", root_dir + "/fonts/openoffice")
base.copy_file(git_dir + "/core-fonts/ASC.ttf", root_dir + "/fonts/ASC.ttf")
base.copy_file(git_dir + "/desktop-apps/common/package/license/3dparty/3DPARTYLICENSE", root_dir + "/3DPARTYLICENSE")
# cef
build_dir_name = "build"
if (0 == platform.find("linux")) and (config.check_option("config", "cef_version_107")):
@ -129,28 +119,41 @@ def make():
else:
base.copy_files(core_dir + "/Common/3dParty/cef/" + native_platform + "/" + build_dir_name + "/*", root_dir)
if (0 == platform.find("mac")):
dir_base_old = os.getcwd()
os.chdir(root_dir + "/Chromium Embedded Framework.framework")
base.create_dir("Versions")
base.create_dir("Versions/A")
base.move_file("Chromium Embedded Framework", "Versions/A/Chromium Embedded Framework")
base.move_dir("Resources", "Versions/A/Resources")
base.move_dir("Libraries", "Versions/A/Libraries")
base.cmd("ln", ["-s", "Versions/A/Chromium Embedded Framework", "Chromium Embedded Framework"])
base.cmd("ln", ["-s", "Versions/A/Resources", "Resources"])
base.cmd("ln", ["-s", "Versions/A/Libraries", "Libraries"])
base.cmd("ln", ["-s", "A", "Versions/Current"])
os.chdir(dir_base_old);
isUseQt = True
if (0 == platform.find("mac")) or (0 == platform.find("ios")):
isUseQt = False
# libraries
base.copy_lib(build_libraries_path, root_dir, "hunspell")
base.copy_lib(build_libraries_path + ("/xp" if isWindowsXP else ""), root_dir, "ooxmlsignature")
base.copy_lib(build_libraries_path + ("/xp" if isWindowsXP else ""), root_dir, "ascdocumentscore")
if (0 != platform.find("mac")):
base.copy_lib(build_libraries_path + ("/xp" if isWindowsXP else ""), root_dir, "qtascdocumentscore")
if (0 == platform.find("mac")):
base.copy_dir(core_build_dir + "/bin/" + platform_postfix + "/editors_helper.app", root_dir + "/editors_helper.app")
else:
base.copy_exe(core_build_dir + "/bin/" + platform_postfix + ("/xp" if isWindowsXP else ""), root_dir, "editors_helper")
if isUseQt:
base.qt_copy_lib("Qt5Core", root_dir)
base.qt_copy_lib("Qt5Gui", root_dir)
base.qt_copy_lib("Qt5PrintSupport", root_dir)
base.qt_copy_lib("Qt5Svg", root_dir)
base.qt_copy_lib("Qt5Widgets", root_dir)
base.qt_copy_lib("Qt5Widgets", root_dir)
base.qt_copy_lib("Qt5Network", root_dir)
base.qt_copy_lib("Qt5OpenGL", root_dir)
@ -159,7 +162,7 @@ def make():
base.qt_copy_plugin("imageformats", root_dir)
base.qt_copy_plugin("platforms", root_dir)
base.qt_copy_plugin("platforminputcontexts", root_dir)
base.qt_copy_plugin("printsupport", root_dir)
base.qt_copy_plugin("printsupport", root_dir)
base.qt_copy_plugin("platformthemes", root_dir)
base.qt_copy_plugin("xcbglintegrations", root_dir)
@ -176,7 +179,7 @@ def make():
base.qt_copy_lib("Qt5DBus", root_dir)
base.qt_copy_lib("Qt5X11Extras", root_dir)
base.qt_copy_lib("Qt5XcbQpa", root_dir)
base.qt_copy_icu(root_dir)
base.qt_copy_icu(root_dir, platform)
if not base.check_congig_option_with_platfom(platform, "libvlc"):
base.copy_files(base.get_env("QT_DEPLOY") + "/../lib/libqgsttools_p.so*", root_dir)
@ -184,8 +187,6 @@ def make():
base.copy_file(git_dir + "/desktop-apps/win-linux/extras/projicons/" + apps_postfix + "/projicons.exe", root_dir + "/DesktopEditors.exe")
if not isWindowsXP:
base.copy_file(git_dir + "/desktop-apps/win-linux/extras/update-daemon/" + apps_postfix + "/updatesvc.exe", root_dir + "/updatesvc.exe")
else:
base.copy_file(git_dir + "/desktop-apps/win-linux/extras/online-installer/" + apps_postfix + "/online-installer.exe", root_dir + "/online-installer.exe")
base.copy_file(git_dir + "/desktop-apps/win-linux/" + apps_postfix + "/DesktopEditors.exe", root_dir + "/editors.exe")
base.copy_file(git_dir + "/desktop-apps/win-linux/res/icons/desktopeditors.ico", root_dir + "/app.ico")
elif (0 == platform.find("linux")):
@ -193,9 +194,9 @@ def make():
if base.check_congig_option_with_platfom(platform, "libvlc"):
vlc_dir = git_dir + "/core/Common/3dParty/libvlc/build/" + platform + "/lib"
if (0 == platform.find("win")):
base.copy_dir(vlc_dir + "/plugins", root_dir + "/plugins")
base.copy_dir(vlc_dir + "/plugins", root_dir + "/plugins")
base.copy_files(vlc_dir + "/*.dll", root_dir)
base.copy_file(vlc_dir + "/vlc-cache-gen.exe", root_dir + "/vlc-cache-gen.exe")
elif (0 == platform.find("linux")):
@ -241,7 +242,22 @@ def make():
#base.copy_dir(git_dir + "/desktop-sdk/ChromiumBasedEditors/plugins/encrypt/ui/common/{14A8FC87-8E26-4216-B34E-F27F053B2EC4}", root_dir + "/editors/sdkjs-plugins/{14A8FC87-8E26-4216-B34E-F27F053B2EC4}")
#base.copy_dir(git_dir + "/desktop-sdk/ChromiumBasedEditors/plugins/encrypt/ui/engine/database/{9AB4BBA8-A7E5-48D5-B683-ECE76A020BB1}", root_dir + "/editors/sdkjs-plugins/{9AB4BBA8-A7E5-48D5-B683-ECE76A020BB1}")
base.copy_sdkjs_plugin(git_dir + "/desktop-sdk/ChromiumBasedEditors/plugins", root_dir + "/editors/sdkjs-plugins", "sendto", True)
isUseAgent = True
if isWindowsXP:
isUseAgent = False
if (0 == platform.find("mac")) and (config.check_option("config", "use_v8")):
isUseAgent = False
if (isUseAgent):
agent_plugin_dir = git_dir + "/desktop-sdk/ChromiumBasedEditors/plugins/ai-agent"
if (False):
base.cmd_in_dir(agent_plugin_dir, "npm", ["install"], True)
base.cmd_in_dir(agent_plugin_dir, "npm", ["run", "build"], True)
base.copy_dir(agent_plugin_dir + "/{9DC93CDB-B576-4F0C-B55E-FCC9C48DD777}", root_dir + "/editors/sdkjs-plugins/{9DC93CDB-B576-4F0C-B55E-FCC9C48DD777}")
else:
base.copy_dir(agent_plugin_dir + "/deploy/{9DC93CDB-B576-4F0C-B55E-FCC9C48DD777}", root_dir + "/editors/sdkjs-plugins/{9DC93CDB-B576-4F0C-B55E-FCC9C48DD777}")
base.copy_file(base_dir + "/js/" + branding + "/desktop/index.html", root_dir + "/index.html")
base.create_dir(root_dir + "/editors/webext")
base.copy_file(base_dir + "/js/" + branding + "/desktop/noconnect.html", root_dir + "/editors/webext/noconnect.html")
@ -254,7 +270,10 @@ def make():
isUseJSC = False
if (0 == platform.find("mac")):
file_size_doctrenderer = os.path.getsize(root_dir + "/converter/libdoctrenderer.dylib")
doctrenderer_lib = "libdoctrenderer.dylib"
if config.check_option("config", "bundle_dylibs"):
doctrenderer_lib = "doctrenderer.framework/doctrenderer"
file_size_doctrenderer = os.path.getsize(root_dir + "/converter/" + doctrenderer_lib)
print("file_size_doctrenderer: " + str(file_size_doctrenderer))
if (file_size_doctrenderer < 5*1024*1024):
isUseJSC = True
@ -268,38 +287,62 @@ def make():
base.delete_file(root_dir + "/cef_sandbox.lib")
base.delete_file(root_dir + "/libcef.lib")
isMacArmPlaformOnIntel = False
is_host_not_arm = False
host_platform = ""
# TODO: fix this on mac_arm64 (qemu)
# on windows we are using qemu
if (platform == "mac_arm64") and not base.is_os_arm():
isMacArmPlaformOnIntel = True
is_host_not_arm = True
host_platform = "mac_64"
# all themes generate ----
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, root_dir + "/converter", "allfontsgen")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, root_dir + "/converter", "allthemesgen")
if (0 == platform.find("mac")):
# gen plists with max_depth 2 because frameworks are only located in root_dir and converter subdirectory
base.for_each_framework(root_dir, "mac", callbacks=[base.generate_plist], max_depth=2)
base.mac_correct_rpath_desktop(root_dir)
if isMacArmPlaformOnIntel:
if is_host_not_arm:
sdkjs_dir = root_dir + "/editors/sdkjs"
end_find_platform = sdkjs_dir.rfind("/mac_arm64/")
sdkjs_dir_mac64 = sdkjs_dir[0:end_find_platform] + "/mac_64/" + sdkjs_dir[end_find_platform+11:]
str1 = "/" + platform + "/"
str2 = "/" + host_platform + "/"
sdkjs_dir_host = sdkjs_dir.replace(str1, str2)
base.delete_dir(sdkjs_dir)
base.copy_dir(sdkjs_dir_mac64, sdkjs_dir)
base.copy_dir(sdkjs_dir_host, sdkjs_dir)
else:
themes_params = []
if ("" != config.option("themesparams")):
themes_params = ["--params=\"" + config.option("themesparams") + "\""]
base.cmd_exe(root_dir + "/converter/allfontsgen", ["--use-system=\"1\"", "--input=\"" + root_dir + "/fonts\"", "--input=\"" + git_dir + "/core-fonts\"", "--allfonts=\"" + root_dir + "/converter/AllFonts.js\"", "--selection=\"" + root_dir + "/converter/font_selection.bin\""])
base.cmd_exe(root_dir + "/converter/allthemesgen", ["--converter-dir=\"" + root_dir + "/converter\"", "--src=\"" + root_dir + "/editors/sdkjs/slide/themes\"", "--allfonts=\"AllFonts.js\"", "--output=\"" + root_dir + "/editors/sdkjs/common/Images\""] + themes_params)
params_allfontsgen = ["--use-system=\"1\"", "--input=\"" + root_dir + "/fonts\"", "--input=\"" + git_dir + "/core-fonts\"", "--allfonts=\"" + root_dir + "/converter/AllFonts.js\"", "--selection=\"" + root_dir + "/converter/font_selection.bin\""]
params_allthemesgen = ["--converter-dir=\"" + root_dir + "/converter\"", "--src=\"" + root_dir + "/editors/sdkjs/slide/themes\"", "--allfonts=\"AllFonts.js\"", "--output=\"" + root_dir + "/editors/sdkjs/common/Images\""] + themes_params
if (0 == platform.find("linux_arm") and not base.is_os_arm()):
x2t_origin = ""
if (config.option("sysroot") != ""):
x2t_origin = base.create_qemu_wrapper(root_dir + "/converter/x2t", platform)
base.cmd_in_dir_qemu(platform, root_dir + "/converter", "./allfontsgen", params_allfontsgen, True)
base.cmd_in_dir_qemu(platform, root_dir + "/converter", "./allthemesgen", params_allthemesgen, True)
if "" != x2t_origin:
base.delete_file(root_dir + "/converter/x2t")
base.move_file(x2t_origin, root_dir + "/converter/x2t")
else:
base.cmd_exe(root_dir + "/converter/allfontsgen", params_allfontsgen, True)
base.cmd_exe(root_dir + "/converter/allthemesgen", params_allthemesgen, True)
base.delete_file(root_dir + "/converter/AllFonts.js")
base.delete_file(root_dir + "/converter/font_selection.bin")
base.delete_file(root_dir + "/converter/fonts.log")
base.delete_exe(root_dir + "/converter/allfontsgen")
base.delete_exe(root_dir + "/converter/allthemesgen")
if not base.is_use_create_artifacts_qemu(platform):
base.delete_exe(root_dir + "/converter/allfontsgen")
base.delete_exe(root_dir + "/converter/allthemesgen")
if not isUseJSC:
base.delete_file(root_dir + "/editors/sdkjs/slide/sdk-all.cache")
return

View File

@ -18,7 +18,7 @@ def deploy_fonts(git_dir, root_dir, platform=""):
if (platform == "android"):
base.copy_dir(git_dir + "/core-fonts/dejavu", root_dir + "/fonts/dejavu")
base.copy_dir(git_dir + "/core-fonts/liberation", root_dir + "/fonts/liberation")
return
return
def make():
base_dir = base.get_script_dir() + "/../out"
@ -35,7 +35,7 @@ def make():
if base.get_env("DESTDIR_BUILD_OVERRIDE") != "":
return
if (base.is_dir(root_dir)):
base.delete_dir(root_dir)
base.create_dir(root_dir)
@ -57,6 +57,7 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "PdfFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "DjVuFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "XpsFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "OFDFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "HtmlFile2")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "doctrenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "Fb2File")
@ -64,6 +65,8 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "IWorkFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "HWPFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "DocxRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "StarMathConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "ooxmlsignature")
base.copy_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", root_dir + "/cmap.bin")
if (0 == platform.find("win") or 0 == platform.find("linux") or 0 == platform.find("mac")):
@ -72,29 +75,14 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "x2t")
# icu
if (0 == platform.find("win")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/icudt58.dll", root_dir + "/icudt58.dll")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/icuuc58.dll", root_dir + "/icuuc58.dll")
if (0 == platform.find("linux")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicudata.so.58", root_dir + "/libicudata.so.58")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicuuc.so.58", root_dir + "/libicuuc.so.58")
if (0 == platform.find("mac")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicudata.58.dylib", root_dir + "/libicudata.58.dylib")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicuuc.58.dylib", root_dir + "/libicuuc.58.dylib")
if (0 == platform.find("android")):
#base.copy_file(core_dir + "/Common/3dParty/icu/android/build/" + platform[8:] + "/libicudata.so", root_dir + "/libicudata.so")
#base.copy_file(core_dir + "/Common/3dParty/icu/android/build/" + platform[8:] + "/libicuuc.so", root_dir + "/libicuuc.so")
base.copy_file(core_dir + "/Common/3dParty/icu/android/build/" + platform[8:] + "/icudt58l.dat", root_dir + "/icudt58l.dat")
base.deploy_icu(core_dir, root_dir, platform)
# js
base.copy_dir(base_dir + "/js/" + branding + "/mobile/sdkjs", root_dir + "/sdkjs")
# correct ios frameworks
if ("ios" == platform):
base.generate_plist(root_dir)
base.for_each_framework(root_dir, "ios", callbacks=[base.generate_plist, base.generate_xcprivacy])
deploy_fonts(git_dir, root_dir)
base.copy_dictionaries(git_dir + "/dictionaries", root_dir + "/dictionaries", True, False)
@ -114,7 +102,7 @@ def make():
deploy_fonts(git_dir, root_dir, "android")
base.copy_dictionaries(git_dir + "/dictionaries", root_dir + "/dictionaries", True, False)
# app
base.generate_doctrenderer_config(root_dir + "/DoctRenderer.config", "./", "builder", "", "./dictionaries")
base.generate_doctrenderer_config(root_dir + "/DoctRenderer.config", "./", "builder", "", "./dictionaries")
libs_dir = root_dir + "/lib"
base.create_dir(libs_dir + "/arm64-v8a")
base.copy_files(base_dir + "/android_arm64_v8a/" + branding + "/mobile/*.so", libs_dir + "/arm64-v8a")

View File

@ -18,7 +18,7 @@ def make():
if base.get_env("DESTDIR_BUILD_OVERRIDE") != "":
return
if (base.is_dir(root_dir)):
base.delete_dir(root_dir)
base.create_dir(root_dir)
@ -37,7 +37,7 @@ def make():
# correct ios frameworks
if ("ios" == platform):
base.generate_plist(root_dir)
base.for_each_framework(root_dir, "ios", callbacks=[base.generate_plist, base.generate_xcprivacy])
for native_platform in platforms:
if native_platform == "android":

View File

@ -41,6 +41,7 @@ def make():
build_server_dir = root_dir + '/server'
server_dir = base.get_script_dir() + "/../../server"
server_admin_panel_dir = base.get_script_dir() + "/../../server-admin-panel"
base.create_dir(build_server_dir + '/DocService')
@ -58,6 +59,15 @@ def make():
base.create_dir(build_server_dir + '/Metrics/node_modules/modern-syslog/build/Release')
base.copy_file(server_dir + "/Metrics/node_modules/modern-syslog/build/Release/core.node", build_server_dir + "/Metrics/node_modules/modern-syslog/build/Release/core.node")
if "server-admin-panel" in base.get_server_addons() and base.is_exist(server_admin_panel_dir):
# AdminPanel server part
base.create_dir(build_server_dir + '/AdminPanel/server')
base.copy_exe(server_admin_panel_dir + "/server", build_server_dir + '/AdminPanel/server', "adminpanel")
# AdminPanel client part
base.create_dir(build_server_dir + '/AdminPanel/client/build')
base.copy_dir(server_admin_panel_dir + '/client/build', build_server_dir + '/AdminPanel/client/build')
qt_dir = base.qt_setup(native_platform)
platform = native_platform
@ -77,6 +87,7 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "PdfFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "DjVuFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "XpsFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "OFDFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "HtmlFile2")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "doctrenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "Fb2File")
@ -84,6 +95,8 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "IWorkFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "HWPFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "DocxRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "StarMathConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "ooxmlsignature")
base.copy_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", converter_dir + "/cmap.bin")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, converter_dir, "x2t")
@ -93,24 +106,18 @@ def make():
base.generate_doctrenderer_config(converter_dir + "/DoctRenderer.config", "../../../", "server", "", "../../../dictionaries")
# icu
if (0 == platform.find("win")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/icudt58.dll", converter_dir + "/icudt58.dll")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/icuuc58.dll", converter_dir + "/icuuc58.dll")
base.deploy_icu(core_dir, converter_dir, platform)
if (0 == platform.find("linux")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicudata.so.58", converter_dir + "/libicudata.so.58")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicuuc.so.58", converter_dir + "/libicuuc.so.58")
if (0 == platform.find("mac")):
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicudata.58.dylib", converter_dir + "/libicudata.58.dylib")
base.copy_file(core_dir + "/Common/3dParty/icu/" + platform + "/build/libicuuc.58.dylib", converter_dir + "/libicuuc.58.dylib")
base.copy_v8_files(core_dir, converter_dir, platform)
# builder
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, converter_dir, "docbuilder")
base.copy_dir(git_dir + "/document-templates/new/en-US", converter_dir + "/empty")
# correct mac frameworks
if (0 == platform.find("mac")):
base.for_each_framework(converter_dir, "mac", callbacks=[base.generate_plist], max_depth=1)
# js
js_dir = root_dir
base.copy_dir(base_dir + "/js/" + branding + "/builder/sdkjs", js_dir + "/sdkjs")
@ -123,7 +130,7 @@ def make():
# add embed worker code
base.cmd_in_dir(git_dir + "/sdkjs/common/embed", "python", ["make.py", js_dir + "/web-apps/apps/api/documents/api.js"])
# plugins
base.create_dir(js_dir + "/sdkjs-plugins")
base.copy_marketplace_plugin(js_dir + "/sdkjs-plugins", False, True)
@ -145,7 +152,7 @@ def make():
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, tools_dir, "allthemesgen")
if ("1" != config.option("preinstalled-plugins")):
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, tools_dir, "pluginsmanager")
branding_dir = server_dir + "/branding"
if("" != config.option("branding") and "onlyoffice" != config.option("branding")):
branding_dir = git_dir + '/' + config.option("branding") + '/server'
@ -176,6 +183,12 @@ def make():
base.copy_dir(document_templates_files + '/new', document_templates + '/new')
base.copy_dir(document_templates_files + '/sample', document_templates + '/sample')
#document-formats
document_formats_files = server_dir + '/../document-formats'
document_formats = build_server_dir + '/../document-formats'
base.create_dir(document_formats)
base.copy_file(document_formats_files + '/onlyoffice-docs-formats.json', document_formats + '/onlyoffice-docs-formats.json')
#license
license_file1 = server_dir + '/LICENSE.txt'
license_file2 = server_dir + '/3rd-Party.txt'
@ -184,6 +197,7 @@ def make():
base.copy_file(license_file1, build_server_dir)
base.copy_file(license_file2, build_server_dir)
base.copy_dir(license_dir, license)
base.copy_dir(server_dir + '/dictionaries', build_server_dir + '/dictionaries')
#branding
welcome_files = branding_dir + '/welcome'
@ -226,4 +240,3 @@ def make():
base.delete_file(root_dir_snap + '/example/nodejs/example')
return

View File

@ -111,7 +111,8 @@ def check_dependencies():
if (host_platform == 'windows'):
checksResult.append(check_nodejs())
if (config.option("sql-type") == 'mysql' and host_platform == 'windows'):
sql_type = config.option("sql-type")
if (sql_type == 'mysql' and host_platform == 'windows'):
checksResult.append(check_mysqlServer())
else:
checksResult.append(check_postgreSQL())
@ -300,9 +301,21 @@ def check_rabbitmq():
print('RabbitMQ is installed')
return dependence
elif (host_platform == 'linux'):
result = base.run_command('service rabbitmq-server status')['stdout']
if (result != ''):
print('Installed RabbitMQ is valid')
result = ''
# Prefer systemctl for systemd
systemctl_result = base.run_command('systemctl status rabbitmq-server')
if systemctl_result['returncode'] == 0 and systemctl_result['stdout']:
result = systemctl_result['stdout']
# Fallback to service for SysV
if result == '':
command_result = base.run_command('service rabbitmq-server status')
if command_result['returncode'] == 0 and command_result['stdout']:
result = command_result['stdout']
if result != '':
print('RabbitMQ is installed')
return dependence
print('RabbitMQ not found')
@ -517,8 +530,9 @@ def check_mysqlServer():
if (host_platform != 'windows'):
result = os.system(mysqlLoginSrt + ' -e "exit"')
if (result == 0):
connectionResult = base.run_command(connectionString)['stdout']
if (connectionResult.find('port') != -1 and connectionResult.find(config.option("db-port")) != -1):
connectionResult = base.run_command(connectionString)
expected_port = config.option("db-port")
if (connectionResult['stdout'].find('port') != -1 and connectionResult['stdout'].find(expected_port) != -1):
print('MySQL configuration is valid')
dependence.sqlPath = 'mysql'
return dependence
@ -533,13 +547,15 @@ def check_mysqlServer():
continue
mysql_full_name = 'MySQL Server ' + info['Version'] + ' '
connectionResult = base.run_command_in_dir(get_mysql_path_to_bin(info['Location']), connectionString)['stdout']
if (connectionResult.find('port') != -1 and connectionResult.find(config.option("db-port")) != -1):
mysql_bin_path = get_mysql_path_to_bin(info['Location'])
connectionResult = base.run_command_in_dir(mysql_bin_path, connectionString)
expected_port = config.option("db-port")
if (connectionResult['stdout'].find('port') != -1 and connectionResult['stdout'].find(expected_port) != -1):
print(mysql_full_name + 'configuration is valid')
dependence.sqlPath = info['Location']
return dependence
print(mysql_full_name + 'configuration is not valid:' + connectionResult)
print(mysql_full_name + 'configuration is not valid')
# if path exists, then further removal and installation fails(according to startup statistics). it is better to fix issue manually.
return dependence
@ -632,8 +648,8 @@ def get_postrgre_path_to_bin(postgrePath = ''):
return postgrePath
def get_postgreLoginSrting(userName):
if (host_platform == 'windows'):
return 'psql -U' + userName + ' '
return 'PGPASSWORD="' + config.option("db-pass") + '" psql -U' + userName + ' -hlocalhost '
return 'psql -U ' + userName + ' -h localhost '
return 'PGPASSWORD="' + config.option("db-pass") + '" psql -U ' + userName + ' -h localhost '
def get_postgreSQLInfoByFlag(flag):
arrInfo = []
@ -667,9 +683,10 @@ def check_postgreSQL():
if (host_platform == 'linux'):
result = os.system(postgreLoginSrt + ' -c "\q"')
connectionResult = base.run_command(connectionString)['stdout']
connectionResult = base.run_command(connectionString)
expected_port = config.option("db-port")
if (result != 0 or connectionResult.find(config.option("db-port")) == -1):
if (result != 0 or connectionResult['stdout'].find(expected_port) == -1):
print('Valid PostgreSQL not found!')
dependence.append_install('PostgreSQL')
dependence.append_uninstall('PostgreSQL')
@ -680,18 +697,20 @@ def check_postgreSQL():
arrInfo = get_postgreSQLInfo()
base.set_env('PGPASSWORD', config.option("db-pass"))
for info in arrInfo:
if (base.is_dir(info['Location']) == False):
continue
postgre_full_name = 'PostgreSQL ' + info['Version'][:2] + ' '
connectionResult = base.run_command_in_dir(get_postrgre_path_to_bin(info['Location']), connectionString)['stdout']
if (connectionResult.find(config.option("db-port")) != -1):
postgre_bin_path = get_postrgre_path_to_bin(info['Location'])
connectionResult = base.run_command_in_dir(postgre_bin_path, connectionString)
expected_port = config.option("db-port")
if (connectionResult['stdout'].find(expected_port) != -1):
print(postgre_full_name + 'configuration is valid')
dependence.sqlPath = info['Location']
return dependence
print(postgre_full_name + 'configuration is not valid')
print('Valid PostgreSQL not found')
@ -704,6 +723,8 @@ def check_postgreSQL():
return dependence
def check_postgreConfig(postgrePath = ''):
result = True
base.print_info('Checking PostgreSQL configuration')
if (host_platform == 'windows'):
base.set_env('PGPASSWORD', config.option("db-pass"))
@ -711,53 +732,78 @@ def check_postgreConfig(postgrePath = ''):
dbUser = config.option("db-user")
dbName = config.option("db-name")
dbPass = config.option("db-pass")
postgre_path_to_bin = get_postrgre_path_to_bin(postgrePath)
postgreLoginRoot = get_postgreLoginSrting(rootUser)
postgreLoginDbUser = get_postgreLoginSrting(dbUser)
creatdb_path = base.get_script_dir() + "/../../server/schema/postgresql/createdb.sql"
if (base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + ' -c "\du ' + dbUser + '"')['stdout'].find(dbUser) != -1):
print('User ' + dbUser + ' is exist')
# Check if user exists
user_check_result = base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + ' -c "\du ' + dbUser + '"')
if (user_check_result['stdout'].find(dbUser) != -1):
# User exists, check password
if (os.system(postgreLoginDbUser + '-c "\q"') != 0):
print('Invalid user password!')
base.print_info('Changing password...')
print('Invalid user password, changing...')
result = change_userPass(dbUser, dbPass, postgre_path_to_bin) and result
else:
print('User ' + dbUser + ' not exist!')
base.print_info('Creating ' + dbName + ' user...')
print('Creating user ' + dbUser + '...')
result = create_postgreUser(dbUser, dbPass, postgre_path_to_bin) and result
if (base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + ' -c "SELECT datname FROM pg_database;"')['stdout'].find(config.option("db-name")) == -1):
print('Database ' + dbName + ' not found')
base.print_info('Creating ' + dbName + ' database...')
result = create_postgreDb(dbName, postgre_path_to_bin) and configureDb(dbUser, dbName, creatdb_path, postgre_path_to_bin)
# Check if database exists
db_check_result = base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + ' -c "SELECT datname FROM pg_database;"')
if (db_check_result['stdout'].find(dbName) == -1):
print('Creating database ' + dbName + '...')
create_result = create_postgreDb(dbName, postgre_path_to_bin)
if create_result:
# Grant privileges to user on database and schema
base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-c "GRANT ALL privileges ON DATABASE ' + dbName + ' TO ' + dbUser + ';"')
base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-d ' + dbName + ' -c "GRANT ALL ON SCHEMA public TO ' + dbUser + ';"')
configure_result = configureDb(dbUser, dbName, creatdb_path, postgre_path_to_bin)
result = create_result and configure_result
else:
result = False
else:
if (base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-c "SELECT pg_size_pretty(pg_database_size(' + "'" + dbName + "'" + '));"')['stdout'].find('7559 kB') != -1):
print('Database ' + dbName + ' not configured')
base.print_info('Configuring ' + dbName + ' database...')
result = configureDb(dbName, creatdb_path, postgre_path_to_bin) and result
print('Database ' + dbName + ' is valid')
if (base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-c "\l+ ' + dbName + '"')['stdout'].find(dbUser +'=CTc/' + rootUser) == -1):
print('User ' + dbUser + ' has no database privileges!')
base.print_info('Setting database privileges for user ' + dbUser + '...')
result = set_dbPrivilegesForUser(dbUser, dbName, postgre_path_to_bin) and result
print('User ' + dbUser + ' has database privileges')
# Database exists - check if tables need to be created
table_count_result = base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-c "SELECT count(*) FROM information_schema.tables WHERE table_schema = \'public\';"')
needs_configure = False
if table_count_result['stdout'].find(' 0') != -1:
# No tables - need to configure
needs_configure = True
if needs_configure:
# Grant privileges and configure
base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-c "GRANT ALL privileges ON DATABASE ' + dbName + ' TO ' + dbUser + ';"')
base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-d ' + dbName + ' -c "GRANT ALL ON SCHEMA public TO ' + dbUser + ';"')
configure_result = configureDb(dbUser, dbName, creatdb_path, postgre_path_to_bin)
result = configure_result and result
return result
def create_postgreDb(dbName, postgre_path_to_bin = ''):
postgreLoginUser = get_postgreLoginSrting(install_params['PostgreSQL']['root'])
if (base.exec_command_in_dir(postgre_path_to_bin, postgreLoginUser + '-c "CREATE DATABASE ' + dbName +';"') != 0):
result = base.run_command_in_dir(postgre_path_to_bin, postgreLoginUser + '-c "CREATE DATABASE ' + dbName +';"')
if (result['returncode'] != 0):
print('Database creation failed!')
return False
return True
def set_dbPrivilegesForUser(userName, dbName, postgre_path_to_bin = ''):
postgreLoginUser = get_postgreLoginSrting(install_params['PostgreSQL']['root'])
if (base.exec_command_in_dir(postgre_path_to_bin, postgreLoginUser + '-c "GRANT ALL privileges ON DATABASE ' + dbName + ' TO ' + userName + ';"') != 0):
result = base.run_command_in_dir(postgre_path_to_bin, postgreLoginUser + '-c "GRANT ALL privileges ON DATABASE ' + dbName + ' TO ' + userName + ';"')
if (result['returncode'] != 0):
print('Grant privileges failed!')
return False
return True
def create_postgreUser(userName, userPass, postgre_path_to_bin = ''):
postgreLoginRoot = get_postgreLoginSrting(install_params['PostgreSQL']['root'])
if (base.exec_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-c "CREATE USER ' + userName + ' WITH password ' + "'" + userPass + "'" + ';"') != 0):
result = base.run_command_in_dir(postgre_path_to_bin, postgreLoginRoot + '-c "CREATE USER ' + userName + ' WITH password ' + "'" + userPass + "'" + ';"')
if (result['returncode'] != 0):
print('User creation failed!')
return False
return True
def change_userPass(userName, userPass, postgre_path_to_bin = ''):
@ -766,13 +812,24 @@ def change_userPass(userName, userPass, postgre_path_to_bin = ''):
return False
return True
def configureDb(userName, dbName, scriptPath, postgre_path_to_bin = ''):
print('Execution ' + scriptPath)
postgreLoginSrt = get_postgreLoginSrting(userName)
code = base.exec_command_in_dir(postgre_path_to_bin, postgreLoginSrt + ' -d ' + dbName + ' -f "' + scriptPath + '"')
if (code != 0):
print('Execution failed!')
print('Executing ' + scriptPath)
if not base.is_file(scriptPath):
print('ERROR: Script file does not exist!')
return False
postgreLoginSrt = get_postgreLoginSrting(userName)
full_command = postgreLoginSrt + ' -d ' + dbName + ' -f "' + scriptPath + '"'
# Use run_command_in_dir to capture output
result = base.run_command_in_dir(postgre_path_to_bin, full_command)
if (result['returncode'] != 0):
print('Execution failed!')
if result['stderr']:
print('Error: ' + result['stderr'])
return False
print('Execution completed')
return True
def uninstall_postgresql():

View File

@ -0,0 +1,342 @@
#!/usr/bin/env python3
"""
Git Operations Script
Provides functionality to clone repositories and create branches.
Uses existing methods from base module and integrates with release.py patterns.
"""
import sys
import argparse
import logging
from typing import Dict
# Add parent directory to path to import modules
sys.path.append('../')
import base
import config
import dependence
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class GitOperations:
"""Class to handle git clone and branch creation using existing base module methods."""
def __init__(self, branding: str = "onlyoffice", base_branch: str = "develop",
branding_url: str = "ONLYOFFICE/onlyoffice.git", branch_name: str = None,
modules: str = "core desktop builder server mobile"):
"""
Initialize GitOperations with branding configuration and configure repositories.
Args:
branding: Branding name (default: onlyoffice)
base_branch: Base branch to work from (default: develop)
branding_url: Relative path from git host base (default: ONLYOFFICE/onlyoffice.git)
branch_name: Name of the branch to create (required for branch operations)
modules: Modules to include (default: core desktop builder server mobile)
"""
self.branding = branding
self.base_branch = base_branch
self.branding_url = branding_url
self.branch_name = branch_name
self.modules = modules
self.work_dir = None
# Configure repositories immediately
self._configure()
# Update repositories after configuration
repositories = self.get_configured_repositories()
#base.update_repositories(repositories)
def create_branch(self, branch_name: str, repo_dir: str = None) -> bool:
"""
Create a new branch using base.cmd_in_dir.
Args:
branch_name: Name of the new branch
repo_dir: Repository directory (optional, uses current if not specified)
from_branch: Branch to create from (optional, uses current if not specified)
Returns:
bool: True if successful, False otherwise
"""
work_dir = repo_dir or self.work_dir
logger.info(f"Creating branch '{branch_name}' in {work_dir}")
try:
# Create and checkout new branch
base.cmd_in_dir(work_dir, "git", ["checkout", "-b", branch_name], True)
logger.info(f"Successfully created branch: {branch_name}")
return True
except SystemExit:
logger.error(f"Failed to create branch: {branch_name}")
return False
def push_branch(self, branch_name: str, repo_dir: str = None, set_upstream: bool = True) -> bool:
"""
Push a branch to remote repository using base.cmd_in_dir.
Args:
branch_name: Name of the branch to push
repo_dir: Repository directory (optional, uses current if not specified)
set_upstream: Whether to set upstream tracking (default: True)
Returns:
bool: True if successful, False otherwise
"""
work_dir = repo_dir or self.work_dir
logger.info(f"Pushing branch '{branch_name}' in {work_dir}")
try:
if set_upstream:
# Push branch and set upstream tracking
base.cmd_in_dir(work_dir, "git", ["push", "-u", "origin", branch_name], True)
else:
# Just push the branch
base.cmd_in_dir(work_dir, "git", ["push", "origin", branch_name], True)
logger.info(f"Successfully pushed branch: {branch_name}")
return True
except SystemExit:
logger.error(f"Failed to push branch: {branch_name}")
return False
def _configure(self) -> bool:
"""
Configure repositories using existing configure.py pattern from release.py.
Returns:
bool: True if successful, False otherwise
"""
logger.info(f"Configuring and cloning repositories for branch: {self.base_branch}")
try:
# Get build_tools origin and construct branding URL from git host base
build_tools_origin = base.git_get_origin()
# Extract git host base (everything up to the host)
# For https://github.com/ORG/build_tools.git -> https://github.com/
# For git@github.com:ORG/build_tools.git -> git@github.com:
if '://' in build_tools_origin: # HTTPS
host_base = build_tools_origin.split('/', 3)[0] + '//' + build_tools_origin.split('/', 3)[2] + '/'
else: # SSH
host_base = build_tools_origin.split(':', 1)[0] + ':'
branding_url = host_base + self.branding_url
logger.info(f"Build tools origin: {build_tools_origin}")
logger.info(f"Git host base: {host_base}")
logger.info(f"Using branding URL: {branding_url}")
# Check platform and dependencies like in release.py
platform = base.host_platform()
if platform == "windows":
dependence.check_pythonPath()
dependence.check_gitPath()
# Run configure.py like in release.py
configure_args = [
'configure.py',
'--branding', self.branding,
'--branding-url', branding_url,
'--branch', self.base_branch,
'--module', self.modules,
'--update', '1',
'--clean', '0'
]
base.cmd_in_dir('../../', 'python', configure_args)
# Parse configuration like in release.py
config.parse()
# Update build_tools repository
base.git_update('build_tools')
# Update branding repository
base.git_update(self.branding)
# Correct defaults (the branding repo is already updated)
config.parse_defaults()
logger.info("Successfully configured")
return True
except Exception as e:
logger.error(f"Failed to configure and clone: {e}")
return False
def get_configured_repositories(self) -> Dict:
"""Get repositories using existing base.get_repositories() pattern from release.py."""
repositories = base.get_repositories()
repositories['core-ext'] = [True, False]
repositories['build_tools'] = [True, False]
repositories[self.branding] = [True, False]
return repositories
def _iterate_repositories(self, operation_func, operation_name: str) -> bool:
"""
Iterate over all repositories and apply the given operation function.
Args:
operation_func: Function to apply to each repository (takes repo_name and repo_path)
operation_name: Name of the operation for logging
Returns:
bool: True if at least one operation succeeded, False otherwise
"""
repositories = self.get_configured_repositories()
success_count = 0
total_count = len(repositories)
for repo_name in repositories:
current_dir = repositories[repo_name][1]
repo_path = f"../../../{repo_name}" if current_dir == False else current_dir
if base.is_dir(repo_path):
if operation_func(repo_name, repo_path):
success_count += 1
else:
logger.warning(f"✗ Failed to {operation_name} in {repo_name}")
else:
logger.warning(f"Repository {repo_name} not found at {repo_path}")
logger.info(f"{operation_name.capitalize()} completed in {success_count}/{total_count} repositories")
return success_count > 0
def delete_branch(self, branch_name: str, repo_dir: str = None, force: bool = False) -> bool:
"""
Delete a branch using base.cmd_in_dir.
Args:
branch_name: Name of the branch to delete
repo_dir: Repository directory (optional, uses current if not specified)
force: Whether to force delete the branch (default: False)
Returns:
bool: True if successful, False otherwise
"""
work_dir = repo_dir or self.work_dir
logger.info(f"Deleting branch '{branch_name}' in {work_dir}")
try:
# Switch to base branch first to avoid deleting current branch
base.cmd_in_dir(work_dir, "git", ["checkout", self.base_branch], True)
# Delete local branch
delete_flag = "-D" if force else "-d"
base.cmd_in_dir(work_dir, "git", ["branch", delete_flag, branch_name], True)
logger.info(f"Successfully deleted local branch: {branch_name}")
# Delete remote branch
try:
base.cmd_in_dir(work_dir, "git", ["push", "origin", "--delete", branch_name], True)
logger.info(f"Successfully deleted remote branch: {branch_name}")
except SystemExit:
logger.warning(f"Failed to delete remote branch: {branch_name} (may not exist)")
return True
except SystemExit:
logger.error(f"Failed to delete branch: {branch_name}")
return False
def create_branches(self) -> bool:
"""
Create a branch with the given name in all repositories.
Returns:
bool: True if successful, False otherwise
"""
logger.info(f"Creating branch '{self.branch_name}' in all repositories")
def create_and_push_branch(repo_name: str, repo_path: str) -> bool:
"""Create and push branch for a single repository."""
if self.create_branch(self.branch_name, repo_path):
logger.info(f"✓ Created branch '{self.branch_name}' in {repo_name}")
# Push the created branch
if self.push_branch(self.branch_name, repo_path):
logger.info(f"✓ Pushed branch '{self.branch_name}' in {repo_name}")
return True
else:
logger.warning(f"✗ Failed to push branch '{self.branch_name}' in {repo_name}")
return False
else:
logger.warning(f"✗ Failed to create branch '{self.branch_name}' in {repo_name}")
return False
try:
return self._iterate_repositories(create_and_push_branch, f"create and push branch '{self.branch_name}'")
except Exception as e:
logger.error(f"Failed to create branch in all repositories: {e}")
return False
def remove_branches(self, force: bool = False) -> bool:
"""
Remove a branch with the given name from all repositories.
Args:
force: Whether to force delete the branch (default: False)
Returns:
bool: True if successful, False otherwise
"""
logger.info(f"Removing branch '{self.branch_name}' from all repositories")
def delete_branch_operation(repo_name: str, repo_path: str) -> bool:
"""Delete branch for a single repository."""
if self.delete_branch(self.branch_name, repo_path, force):
logger.info(f"✓ Removed branch '{self.branch_name}' from {repo_name}")
return True
else:
logger.warning(f"✗ Failed to remove branch '{self.branch_name}' from {repo_name}")
return False
try:
return self._iterate_repositories(delete_branch_operation, f"remove branch '{self.branch_name}'")
except Exception as e:
logger.error(f"Failed to remove branch from all repositories: {e}")
return False
def main():
"""Main function to handle command line arguments."""
parser = argparse.ArgumentParser(description='Git Operations Tool - Create and Remove Branches')
subparsers = parser.add_subparsers(dest='command', help='Available commands')
# Create branch command (configure, clone and create branch in all repositories)
branch_parser = subparsers.add_parser('create', help='Configure, clone and create branch in all repositories')
branch_parser.add_argument('branch_name', help='Name of the branch to create')
branch_parser.add_argument('--base-branch', default='develop', help='Base branch to work from (default: develop)')
branch_parser.add_argument('--branding', default='onlyoffice', help='Branding name')
branch_parser.add_argument('--branding-url', default='ONLYOFFICE/onlyoffice.git', help='Relative path from git host base (default: ONLYOFFICE/onlyoffice.git)')
branch_parser.add_argument('--modules', default='core desktop builder server mobile', help='Modules to include')
# Remove branch command (configure, clone and remove branch from all repositories)
remove_parser = subparsers.add_parser('remove', help='Configure, clone and remove branch from all repositories')
remove_parser.add_argument('branch_name', help='Name of the branch to remove')
remove_parser.add_argument('--base-branch', default='develop', help='Base branch to work from (default: develop)')
remove_parser.add_argument('--branding', default='onlyoffice', help='Branding name')
remove_parser.add_argument('--branding-url', default='ONLYOFFICE/onlyoffice.git', help='Relative path from git host base (default: ONLYOFFICE/onlyoffice.git)')
remove_parser.add_argument('--modules', default='core desktop builder server mobile', help='Modules to include')
remove_parser.add_argument('--force', action='store_true', help='Force delete the branch (equivalent to git branch -D)')
args = parser.parse_args()
if not args.command:
parser.print_help()
return
git_ops = GitOperations(args.branding, args.base_branch, args.branding_url, args.branch_name, args.modules)
if args.command == 'create':
success = git_ops.create_branches()
sys.exit(0 if success else 1)
elif args.command == 'remove':
success = git_ops.remove_branches(args.force)
sys.exit(0 if success else 1)
if __name__ == '__main__':
main()

32
scripts/min.py Normal file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env python
import sys
sys.path.append('../../build_tools/scripts')
import base
import os
args = sys.argv[1:]
if (1 > len(args)):
print("Please use min.py PATH_TO_SCRIPT.js")
exit(0)
script_path = args[0]
script_path = os.path.abspath(script_path)
script_dir = os.path.dirname(script_path)
script_name = os.path.splitext(os.path.basename(script_path))[0]
script_path_min = os.path.join(script_dir, script_name + ".min.js")
#compilation_level = "WHITESPACE_ONLY"
compilation_level = "SIMPLE_OPTIMIZATIONS"
base.cmd("java", ["-jar", "../../sdkjs/build/node_modules/google-closure-compiler-java/compiler.jar",
"--compilation_level", compilation_level,
"--js_output_file", script_path_min,
"--js", script_path])
dev_content = base.readFile(script_path)
license = dev_content[0:dev_content.find("*/")+2]
min_content = base.readFile(script_path_min)
base.delete_file(script_path_min)
base.writeFile(script_path_min, license + "\n\n" + min_content)

View File

@ -30,23 +30,6 @@ if utils.is_macos():
builder_product_name = "Document Builder"
if utils.is_linux():
builder_make_targets = [
{
"make": "tar",
"src": "tar/*.tar*",
"dst": "builder/linux/generic/"
},
{
"make": "deb",
"src": "deb/*.deb",
"dst": "builder/linux/debian/"
},
{
"make": "rpm",
"src": "rpm/build/RPMS/*/*.rpm",
"dst": "builder/linux/rhel/"
}
]
desktop_make_targets = [
{
"make": "tar",

View File

@ -15,9 +15,9 @@ def make():
if utils.is_windows():
make_windows()
elif utils.is_macos():
make_macos()
make_macos_linux()
elif utils.is_linux():
make_linux()
make_macos_linux()
return
def s3_upload(files, dst):
@ -74,15 +74,14 @@ def make_windows():
if common.clean:
utils.log_h2("builder clean")
utils.delete_dir("build")
utils.delete_files("exe\\*.exe")
utils.delete_files("zip\\*.msi")
utils.delete_dir("zip")
if make_prepare():
make_zip()
make_inno()
make_wheel()
else:
utils.set_summary("builder zip build", False)
utils.set_summary("builder inno build", False)
utils.set_summary("builder python wheel build", False)
utils.set_cwd(common.workspace_dir)
return
@ -118,33 +117,24 @@ def make_zip():
utils.set_summary("builder zip deploy", ret)
return
def make_inno():
args = [
"-Version", package_version,
"-Arch", arch
]
if not branding.onlyoffice:
args += ["-Branding", common.branding]
if common.sign:
args += ["-Sign"]
utils.log_h2("builder inno build")
ret = utils.ps1("make_inno.ps1", args, verbose=True)
utils.set_summary("builder inno build", ret)
if common.deploy and ret:
utils.log_h2("builder inno deploy")
ret = s3_upload(utils.glob_path("exe/*.exe"), "builder/win/inno/")
utils.set_summary("builder inno deploy", ret)
return
def make_macos():
def make_macos_linux():
utils.set_cwd("document-builder-package")
make_tar()
make_wheel()
utils.set_cwd(common.workspace_dir)
return
def make_tar():
utils.log_h2("builder tar build")
make_args = ["tar"]
if common.platform == "darwin_arm64":
make_args += ["-e", "UNAME_M=arm64"]
if common.platform == "darwin_x86_64":
make_args += ["-e", "UNAME_M=x86_64"]
if common.platform == "linux_aarch64":
make_args += ["-e", "UNAME_M=aarch64"]
if not branding.onlyoffice:
make_args += ["-e", "BRANDING_DIR=../" + common.branding + "/document-builder-package"]
ret = utils.sh("make clean && make " + " ".join(make_args), verbose=True)
@ -152,29 +142,94 @@ def make_macos():
if common.deploy:
utils.log_h2("builder tar deploy")
ret = s3_upload(utils.glob_path("tar/*.tar.xz"), "builder/mac/generic/")
if utils.is_macos():
s3_dest = "builder/mac/generic/"
elif utils.is_linux():
s3_dest = "builder/linux/generic/"
ret = s3_upload(utils.glob_path("tar/*.tar.xz"), s3_dest)
utils.set_summary("builder tar deploy", ret)
utils.set_cwd(common.workspace_dir)
return
def make_linux():
utils.set_cwd("document-builder-package")
def make_wheel():
platform_tags = {
"windows_x64": "win_amd64",
"windows_x86": "win32",
"darwin_arm64": "macosx_11_0_arm64",
"darwin_x86_64": "macosx_10_9_x86_64",
"linux_x86_64": "manylinux_2_23_x86_64",
"linux_aarch64": "manylinux_2_23_aarch64"
}
utils.log_h2("builder build")
make_args = [t["make"] for t in branding.builder_make_targets]
if common.platform == "linux_aarch64":
make_args += ["-e", "UNAME_M=aarch64"]
if not branding.onlyoffice:
make_args += ["-e", "BRANDING_DIR=../" + common.branding + "/document-builder-package"]
ret = utils.sh("make clean && make " + " ".join(make_args), verbose=True)
utils.set_summary("builder build", ret)
if not common.platform in platform_tags: return
if common.deploy:
for t in branding.builder_make_targets:
utils.log_h2("builder " + t["make"] + " deploy")
ret = s3_upload(utils.glob_path(t["src"]), t["dst"])
utils.set_summary("builder " + t["make"] + " deploy", ret)
utils.log_h2("builder python wheel build")
builder_dir = "build"
if utils.is_linux():
builder_dir = "build/opt/onlyoffice/documentbuilder"
utils.delete_dir("python")
utils.copy_dir("../onlyoffice/build_tools/packaging/docbuilder/resources", "python")
utils.copy_dir(builder_dir, "python/docbuilder/lib", True, True)
desktop_dir = "../desktop-apps/macos/build/ONLYOFFICE.app/Contents/Resources/converter"
if utils.is_macos() and "desktop" in common.targets and utils.is_exist(desktop_dir):
for f in utils.glob_path(desktop_dir + "/*.dylib") + [desktop_dir + "/x2t"]:
utils.copy_file(f, builder_dir + "/" + utils.get_basename(f))
old_cwd = utils.get_cwd()
utils.set_cwd("python/docbuilder")
if not utils.is_file("docbuilder.py"):
utils.copy_file("lib/docbuilder.py", "docbuilder.py")
# fix docbuilder.py
content = ""
with open("docbuilder.py", "r") as file:
content = file.read()
old_line = "builder_path = os.path.dirname(os.path.realpath(__file__))"
new_line = "builder_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), \"lib\")"
content = content.replace(old_line, new_line)
with open("docbuilder.py", "w") as file:
file.write(content)
# remove unnecessary files
utils.set_cwd("lib")
utils.delete_dir("include")
utils.delete_file("build.date")
utils.delete_file("docbuilder.jar")
utils.delete_file("docbuilder.py")
if utils.is_windows():
utils.delete_file("doctrenderer.lib")
utils.delete_file("docbuilder.com.dll")
utils.delete_file("docbuilder.net.dll")
utils.delete_file("docbuilder.jni.dll")
elif utils.is_macos():
if (utils.is_file("libdocbuilder.jni.dylib")):
utils.delete_file("libdocbuilder.jni.dylib")
if (utils.is_dir("docbuilder.jni.framework")):
utils.delete_file("docbuilder.jni.framework")
utils.remove_all_symlinks(".")
elif utils.is_linux():
utils.delete_file("libdocbuilder.jni.so")
utils.set_env("DOCBUILDER_VERSION", common.version + "." + common.build)
platform = "linux_64"
utils.set_cwd("../..")
plat_name = platform_tags[common.platform]
ret = utils.sh("python setup.py bdist_wheel --plat-name " + plat_name + " --python-tag py2.py3", verbose=True)
utils.set_summary("builder python wheel build", ret)
if common.deploy and ret:
utils.log_h2("builder python wheel deploy")
if utils.is_windows():
s3_dest = "builder/win/python/"
elif utils.is_macos():
s3_dest = "builder/mac/python/"
elif utils.is_linux():
s3_dest = "builder/linux/python/"
ret = s3_upload(utils.glob_path("dist/*.whl"), s3_dest)
utils.set_summary("builder python wheel deploy", ret)
utils.set_cwd(old_cwd)
utils.set_cwd(common.workspace_dir)
return

View File

@ -3,14 +3,14 @@
platformPrefixes = {
"windows_x64": "win_64",
"windows_x86": "win_32",
"windows_arm64": "win_arm64",
"windows_x64_xp": "win_64_xp",
"windows_x86_xp": "win_32_xp",
"darwin_x86_64": "mac_64",
"darwin_arm64": "mac_arm64",
"darwin_x86_64": "mac_64",
"darwin_x86_64_v8": "mac_64",
"linux_x86_64": "linux_64",
"linux_aarch64": "linux_arm64",
"linux_x86_64_cef": "linux_64",
}
out_dir = "build_tools/out"

View File

@ -35,8 +35,8 @@ def s3_upload(files, dst):
#
def make_windows():
global package_name, package_version, arch, xp, suffix
utils.set_cwd("desktop-apps\\win-linux\\package\\windows")
global package_name, package_version, arch, xp
utils.set_cwd("desktop-apps\\package")
package_name = branding.desktop_package_name
package_version = common.version + "." + common.build
@ -44,150 +44,129 @@ def make_windows():
"windows_x64": "x64",
"windows_x64_xp": "x64",
"windows_x86": "x86",
"windows_x86_xp": "x86"
"windows_x86_xp": "x86",
"windows_arm64": "arm64"
}[common.platform]
xp = common.platform.endswith("_xp")
suffix = arch + ("-xp" if xp else "")
if common.clean:
utils.log_h2("desktop clean")
utils.delete_dir("DesktopEditors-cache")
utils.delete_files("*.exe")
utils.delete_files("*.msi")
utils.delete_files("*.aic")
utils.delete_files("*.tmp")
utils.delete_files("*.zip")
utils.delete_files("data\\*.exe")
utils.delete_dir("build")
utils.delete_files("inno\\package.config")
utils.delete_files("inno\\*.exe")
utils.delete_dir("advinst\\DesktopEditors-cache")
utils.delete_files("advinst\\package.config")
utils.delete_files("advinst\\*.msi")
utils.delete_files("advinst\\*.aic")
utils.delete_dir("zip")
make_prepare()
make_zip()
make_inno()
make_advinst()
make_online()
if not xp:
make_prepare()
make_zip()
if branding.onlyoffice:
make_inno()
make_inno("standalone")
make_advinst()
make_prepare("commercial")
make_zip("commercial")
make_inno("commercial")
make_advinst("commercial")
else:
make_prepare("xp")
make_zip("xp")
make_inno("xp")
utils.set_cwd(common.workspace_dir)
return
def make_prepare():
def make_prepare(edition = "opensource"):
args = [
"-Version", package_version,
"-Arch", arch
"-Arch", arch,
"-Target", edition,
"-CompanyName", branding.company_name
]
if xp:
args += ["-Target", "xp"]
if common.sign:
args += ["-Sign"]
utils.log_h2("desktop prepare")
utils.log_h2("desktop prepare " + edition)
ret = utils.ps1("make.ps1", args, verbose=True)
utils.set_summary("desktop prepare", ret)
utils.set_summary("desktop prepare " + edition, ret)
return
def make_zip():
zip_file = "%s-%s-%s.zip" % (package_name, package_version, suffix)
def make_zip(edition = "opensource"):
if edition == "commercial": zip_file = "%s-Enterprise-%s-%s.zip"
elif edition == "xp": zip_file = "%s-XP-%s-%s.zip"
else: zip_file = "%s-%s-%s.zip"
zip_file = "zip\\" + zip_file % (package_name, package_version, arch)
args = [
"-Version", package_version,
"-Arch", arch
"-Arch", arch,
"-Target", edition,
"-CompanyName", branding.company_name
]
if xp:
args += ["-Target", "xp"]
# if common.sign:
# args += ["-Sign"]
utils.log_h2("desktop zip build")
utils.log_h2("desktop zip " + edition + " build")
ret = utils.ps1("make_zip.ps1", args, verbose=True)
utils.set_summary("desktop zip build", ret)
utils.set_summary("desktop zip " + edition + " build", ret)
if common.deploy and ret:
utils.log_h2("desktop zip deploy")
utils.log_h2("desktop zip " + edition + " deploy")
ret = s3_upload([zip_file], "desktop/win/generic/")
utils.set_summary("desktop zip deploy", ret)
utils.set_summary("desktop zip " + edition + " deploy", ret)
return
def make_inno():
inno_file = "%s-%s-%s.exe" % (package_name, package_version, suffix)
inno_sa_file = "%s-Standalone-%s-%s.exe" % (package_name, package_version, suffix)
inno_update_file = "%s-Update-%s-%s.exe" % (package_name, package_version, suffix)
update_wrapper = not (hasattr(branding, 'desktop_updates_skip_iss_wrapper') and branding.desktop_updates_skip_iss_wrapper)
def make_inno(edition = "opensource"):
if edition == "commercial": inno_file = "%s-Enterprise-%s-%s.exe"
elif edition == "standalone": inno_file = "%s-Standalone-%s-%s.exe"
elif edition == "update": inno_file = "%s-Update-%s-%s.exe"
elif edition == "xp": inno_file = "%s-XP-%s-%s.exe"
else: inno_file = "%s-%s-%s.exe"
inno_file = "inno\\" + inno_file % (package_name, package_version, arch)
args = [
"-Version", package_version,
"-Arch", arch
"-Arch", arch,
"-Target", edition
]
if common.sign:
args += ["-Sign"]
utils.log_h2("desktop inno build")
if xp:
ret = utils.ps1("make_inno.ps1", args + ["-Target", "xp"], verbose=True)
else:
ret = utils.ps1("make_inno.ps1", args, verbose=True)
utils.set_summary("desktop inno build", ret)
args += ["-TimestampServer", "http://timestamp.comodoca.com/authenticode"]
if branding.onlyoffice and not xp:
utils.log_h2("desktop inno standalone")
ret = utils.ps1("make_inno.ps1", args + ["-Target", "standalone"], verbose=True)
utils.set_summary("desktop inno standalone build", ret)
utils.log_h2("desktop inno " + edition + " build")
ret = utils.ps1("make_inno.ps1", args, verbose=True)
utils.set_summary("desktop inno " + edition + " build", ret)
if update_wrapper:
utils.log_h2("desktop inno update build")
if xp:
ret = utils.ps1("make_inno.ps1", args + ["-Target", "xp_update"], verbose=True)
else:
ret = utils.ps1("make_inno.ps1", args + ["-Target", "update"], verbose=True)
utils.set_summary("desktop inno update build", ret)
if common.deploy:
utils.log_h2("desktop inno deploy")
if common.deploy and ret:
utils.log_h2("desktop inno " + edition + " deploy")
ret = s3_upload([inno_file], "desktop/win/inno/")
utils.set_summary("desktop inno deploy", ret)
if branding.onlyoffice and not xp:
utils.log_h2("desktop inno standalone deploy")
ret = s3_upload([inno_sa_file], "desktop/win/inno/")
utils.set_summary("desktop inno standalone deploy", ret)
utils.log_h2("desktop inno update deploy")
if utils.is_file(inno_update_file):
ret = s3_upload([inno_update_file], "desktop/win/inno/")
elif utils.is_file(inno_file):
ret = s3_upload([inno_file], "desktop/win/inno/" + inno_update_file)
else:
ret = False
utils.set_summary("desktop inno update deploy", ret)
utils.set_summary("desktop inno " + edition + " deploy", ret)
return
def make_advinst():
if not common.platform in ["windows_x64", "windows_x86"]:
return
advinst_file = "%s-%s-%s.msi" % (package_name, package_version, suffix)
def make_advinst(edition = "opensource"):
if edition == "commercial": advinst_file = "%s-Enterprise-%s-%s.msi"
else: advinst_file = "%s-%s-%s.msi"
advinst_file = "advinst\\" + advinst_file % (package_name, package_version, arch)
args = [
"-Version", package_version,
"-Arch", arch
"-Arch", arch,
"-Target", edition
]
if common.sign:
args += ["-Sign"]
utils.log_h2("desktop advinst build")
utils.log_h2("desktop advinst " + edition + " build")
ret = utils.ps1("make_advinst.ps1", args, verbose=True)
utils.set_summary("desktop advinst build", ret)
utils.set_summary("desktop advinst " + edition + " build", ret)
if common.deploy and ret:
utils.log_h2("desktop advinst deploy")
utils.log_h2("desktop advinst " + edition + " deploy")
ret = s3_upload([advinst_file], "desktop/win/advinst/")
utils.set_summary("desktop advinst deploy", ret)
return
def make_online():
if not common.platform in ["windows_x86_xp"]:
return
online_file = "%s-%s-%s.exe" % ("OnlineInstaller", package_version, suffix)
ret = utils.is_file(online_file)
utils.set_summary("desktop online installer build", ret)
if common.deploy and ret:
utils.log_h2("desktop online installer deploy")
ret = s3_upload([online_file], "desktop/win/online/")
utils.set_summary("desktop online installer deploy", ret)
utils.set_summary("desktop advinst " + edition + " deploy", ret)
return
#
@ -196,7 +175,7 @@ def make_online():
def make_macos():
global package_name, build_dir, branding_dir, updates_dir, changes_dir, \
suffix, lane, scheme, released_updates_dir
suffix, lane, scheme, source_dir, released_updates_dir
package_name = branding.desktop_package_name
build_dir = branding.desktop_build_dir
branding_dir = branding.desktop_branding_dir
@ -261,32 +240,34 @@ def make_macos():
dmg = make_dmg()
if dmg and sparkle_updates:
make_sparkle_updates()
if common.platform != "darwin_x86_64_v8":
make_dmg("commercial")
utils.set_cwd(common.workspace_dir)
return
def make_dmg():
utils.log_h2("desktop dmg build")
utils.log_h3(scheme)
def make_dmg(target = "opensource"):
utils.log_h2("desktop dmg " + target + " build")
utils.log_h3("build/" + package_name + ".app")
dmg = utils.sh(
"bundler exec fastlane " + lane + " skip_git_bump:true",
verbose=True
)
utils.set_summary("desktop dmg build", dmg)
args = ["bundler", "exec", "fastlane", lane, "skip_git_bump:true"]
if target == "commercial":
args += ["edition:Enterprise"]
dmg = utils.sh(" ".join(args), verbose=True)
utils.set_summary("desktop dmg " + target + " build", dmg)
if common.deploy and dmg:
utils.log_h2("desktop dmg deploy")
utils.log_h2("desktop dmg " + target + " deploy")
ret = s3_upload(
utils.glob_path("build/*.dmg"),
"desktop/mac/%s/%s/%s/" % (suffix, common.version, common.build))
utils.set_summary("desktop dmg deploy", ret)
utils.log_h2("desktop zip deploy")
if common.deploy and dmg and target != "commercial":
utils.log_h2("desktop zip " + target + " deploy")
ret = s3_upload(
["build/%s-%s.zip" % (scheme, common.version)],
"desktop/mac/%s/%s/%s/" % (suffix, common.version, common.build))
utils.set_summary("desktop zip deploy", ret)
utils.set_summary("desktop zip " + target + " deploy", ret)
return dmg
def make_sparkle_updates():
@ -334,22 +315,30 @@ def make_sparkle_updates():
#
def make_linux():
utils.set_cwd("desktop-apps/win-linux/package/linux")
utils.set_cwd("desktop-apps/package")
utils.log_h2("desktop build")
make_args = [t["make"] for t in branding.desktop_make_targets]
if common.platform == "linux_aarch64":
make_args += ["-e", "UNAME_M=aarch64"]
if not branding.onlyoffice:
make_args += ["-e", "BRANDING_DIR=../../../../" + common.branding + "/desktop-apps/win-linux/package/linux"]
ret = utils.sh("make clean && make " + " ".join(make_args), verbose=True)
utils.set_summary("desktop build", ret)
for edition in ["opensource", "commercial"]:
utils.log_h2("desktop " + edition + " build")
make_args = [t["make"] for t in branding.desktop_make_targets]
if edition == "commercial":
make_args += ["-e", "PACKAGE_EDITION=commercial"]
if common.platform == "linux_aarch64":
make_args += ["-e", "UNAME_M=aarch64"]
if not branding.onlyoffice:
make_args += ["-e", "BRANDING_DIR=../../" + common.branding + "/desktop-apps/package"]
ret = utils.sh("make clean && make " + " ".join(make_args), verbose=True)
utils.set_summary("desktop " + edition + " build", ret)
if common.deploy:
for t in branding.desktop_make_targets:
utils.log_h2("desktop " + t["make"] + " deploy")
ret = s3_upload(utils.glob_path(t["src"]), t["dst"])
utils.set_summary("desktop " + t["make"] + " deploy", ret)
if common.deploy:
for t in branding.desktop_make_targets:
utils.log_h2("desktop " + edition + " " + t["make"] + " deploy")
uploads = []
for f in utils.glob_path(t["src"]):
if "help" in f and not \
("x86_64" in common.platform and edition == "opensource"): continue
uploads.append(f)
ret = s3_upload(uploads, t["dst"])
utils.set_summary("desktop " + edition + " " + t["make"] + " deploy", ret)
utils.set_cwd(common.workspace_dir)
return

View File

@ -201,12 +201,12 @@ def copy_files(src, dst, override=True, verbose=True):
copy_files(file + "/*", dst + "/" + file_name, override)
return
def copy_dir(src, dst, verbose=True):
def copy_dir(src, dst, verbose=True, symlinks=False):
if verbose:
log("- copy_dir:")
log(" src: " + src)
log(" dst: " + dst)
shutil.copytree(src, dst)
shutil.copytree(src, dst, symlinks=symlinks)
return
def copy_dir_content(src, dst, filter_include = "", filter_exclude = "", verbose=True):
@ -262,6 +262,20 @@ def delete_files(src, verbose=True):
shutil.rmtree(path, ignore_errors=True)
return
def remove_all_symlinks(dir):
for root, dirs, files in os.walk(dir, topdown=True, followlinks=False):
for name in files:
path = os.path.join(root, name)
if os.path.islink(path):
os.unlink(path)
for name in list(dirs):
path = os.path.join(root, name)
if os.path.islink(path):
os.unlink(path)
dirs.remove(name)
return
def set_summary(target, status):
common.summary.append({target: status})
return

View File

@ -25,7 +25,7 @@ def get_j_num():
def check_support_platform(platform):
qt_dir = base.qt_setup(platform)
if not base.is_file(qt_dir + "/bin/qmake") and not base.is_file(qt_dir + "/bin/qmake.exe"):
if not base.is_file(qt_dir + "/bin/qmake") and not base.is_file(qt_dir + "/bin/qmake.exe") and not base.is_file(qt_dir + "/bin/qmake.bat"):
return False
return True
@ -36,7 +36,7 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
return
old_env = dict(os.environ)
# qt
qt_dir = base.qt_setup(platform)
base.set_env("OS_DEPLOY", platform)
@ -50,7 +50,7 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
if (pro_dir.endswith("/")):
pro_dir = pro_dir[:-1]
makefile_name = "Makefile." + get_make_file_suffix(platform)
makefile_name = "Makefile." + get_make_file_suffix(platform)
makefile = pro_dir + "/" + makefile_name
stash_file = pro_dir + "/.qmake.stash"
@ -73,6 +73,11 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
# setup ios env
if (-1 != platform.find("ios")):
base.hack_xcode_ios()
sdk_name = "iphoneos"
if qmake_config_addon.find("ios_simulator") != -1:
sdk_name = "iphonesimulator"
base.set_env("SDK_PATH", base.find_ios_sdk(sdk_name))
base.set_env("XCODE_TOOLCHAIN_PATH", base.find_xcode_toolchain(sdk_name))
if base.is_file(makefile):
base.delete_file(makefile)
@ -91,19 +96,44 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
build_params = ["-nocache", file_pro] + base.qt_config_as_param(config_param) + qmake_addon
qmake_app = qt_dir + "/bin/qmake"
# non windows platform
if not base.is_windows():
if base.is_file(qt_dir + "/onlyoffice_qt.conf"):
build_params.append("-qtconf")
build_params.append(qt_dir + "/onlyoffice_qt.conf")
base.cmd(qmake_app, build_params)
if "1" == config.option("use-clang"):
build_params.append("-spec")
build_params.append("linux-clang-libc++")
if "" != config.option("sysroot"):
sysroot_path = config.option("sysroot_" + platform)
os.environ['QMAKE_CUSTOM_SYSROOT'] = sysroot_path
os.environ['QMAKE_CUSTOM_SYSROOT_BIN'] = config.get_custom_sysroot_bin(platform)
os.environ['PKG_CONFIG_PATH'] = config.get_custom_sysroot_lib(platform, True) + "/pkgconfig"
os.environ['PKG_CONFIG_SYSROOT_DIR'] = sysroot_path
base.cmd_exe(qmake_app, build_params)
if "" != config.option("sysroot"):
base.set_sysroot_env(platform)
base.correct_makefile_after_qmake(platform, makefile)
if ("1" == config.option("clean")):
base.cmd_and_return_cwd("make", clean_params, True)
base.cmd_and_return_cwd("make", distclean_params, True)
base.cmd(qmake_app, build_params)
if "" != config.option("sysroot"):
base.restore_sysroot_env()
base.cmd(qmake_app, build_params)
if "" != config.option("sysroot"):
base.set_sysroot_env(platform)
base.correct_makefile_after_qmake(platform, makefile)
base.cmd_and_return_cwd("make", ["-f", makefile] + get_j_num(), is_no_errors)
if "" != config.option("sysroot"):
base.restore_sysroot_env()
else:
config_params_array = base.qt_config_as_param(config_param)
config_params_string = ""
@ -113,12 +143,22 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
if ("" != qmake_addon_string):
qmake_addon_string = " " + qmake_addon_string
vcvarsall_arch = "x64"
if base.platform_is_32(platform):
vcvarsall_arch = "x86"
if (platform == "win_arm64"):
vcvarsall_arch = "x64_arm64"
qmake_env_addon = base.get_env("QT_QMAKE_ADDON")
if (qmake_env_addon != ""):
qmake_env_addon += " "
qmake_bat = []
qmake_bat.append("call \"" + config.option("vs-path") + "/vcvarsall.bat\" " + ("x86" if base.platform_is_32(platform) else "x64"))
qmake_bat.append("call \"" + config.option("vs-path") + "/vcvarsall.bat\" " + vcvarsall_arch)
qmake_addon_string = ""
if ("" != config.option("qmake_addon")):
qmake_addon_string = " " + (" ").join(["\"" + addon + "\"" for addon in qmake_addon])
qmake_bat.append("call \"" + qmake_app + "\" -nocache " + file_pro + config_params_string + qmake_addon_string)
qmake_bat.append("call \"" + qmake_app + "\" -nocache " + qmake_env_addon + file_pro + config_params_string + qmake_addon_string)
if ("1" == config.option("clean")):
qmake_bat.append("call nmake " + " ".join(clean_params))
qmake_bat.append("call nmake " + " ".join(distclean_params))

View File

@ -13,7 +13,7 @@ def writeFile(path, content):
if (os.path.isfile(path)):
os.remove(path)
with open(path, "w") as file:
with open(path, "w", encoding='utf-8') as file:
file.write(content)
return
@ -160,6 +160,12 @@ class EditorApi(object):
editors_support = decoration[index_type_editors:index_type_editors_end]
if -1 == editors_support.find(self.type):
return
decoration = "\n".join(
line for line in decoration.splitlines()
if "@typeofeditors" not in line and "@see" not in line
)
# optimizations for first file
if 0 == self.numfile:
self.records.append(decoration + "\n" + code + "\n")
@ -208,7 +214,7 @@ if __name__ == "__main__":
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default="../../../onlyoffice.github.io\sdkjs-plugins\content\macros\libs/" # Default value
default="../../../web-apps/vendor/monaco/libs/" # Default value
)
args = parser.parse_args()
@ -217,7 +223,7 @@ if __name__ == "__main__":
if True == os.path.isdir(args.destination):
shutil.rmtree(args.destination, ignore_errors=True)
os.mkdir(args.destination)
convert_to_interface(["word/apiBuilder.js"], "word")
convert_to_interface(["word/apiBuilder.js", "../sdkjs-forms/apiBuilder.js"], "word")
convert_to_interface(["word/apiBuilder.js", "slide/apiBuilder.js"], "slide")
convert_to_interface(["word/apiBuilder.js", "slide/apiBuilder.js", "cell/apiBuilder.js"], "cell")
os.chdir(old_cur)

View File

@ -1,13 +1,20 @@
# Documentation Generation Guide
This guide explains how to generate documentation for Onlyoffice Builder/Plugins API using the provided Python scripts: `generate_docs_json.py`, `generate_docs_plugins_json.py`, `generate_docs_md.py`. These scripts are used to create JSON and Markdown documentation for the `apiBuilder.js` files from the word, cell, and slide editors.
This guide explains how to generate documentation for Onlyoffice Builder
and Plugins (Methods/Events) API using the following Python scripts:
- `office-api/generate_docs_json.py`
- `office-api/generate_docs_md.py`
- `plugins/generate_docs_methods_json.py`
- `plugins/generate_docs_methods_md.py`
- `plugins/generate_docs_events_json.py`
- `plugins/generate_docs_events_md.py`
## Requirements
```bash
Node.js v20 and above
Python v3.10 and above
Python v3.12 and above
```
## Installation
@ -20,61 +27,112 @@ npm install
## Scripts Overview
### `generate_docs_json.py`
### `office-api/generate_docs_json.py`
This script generates JSON documentation based on the `apiBuilder.js` files.
- **Usage**:
```bash
python generate_docs_json.py output_path
```
- **Parameters**:
- `output_path` (optional): The directory where the JSON documentation will be saved. If not specified, the default path is `../../../../office-js-api-declarations/office-js-api`.
- `output_path` (optional): The directory where the JSON documentation
will be saved. If not specified, the default path is
`../../../../office-js-api-declarations/office-js-api`.
### `generate_docs_plugins_json.py`
This script generates JSON documentation based on the `api_plugins.js` files.
- **Usage**:
```bash
python generate_docs_plugins_json.py output_path
```
- **Parameters**:
- `output_path` (optional): The directory where the JSON documentation will be saved. If not specified, the default path is `../../../../office-js-api-declarations/office-js-api-plugins`.
### `generate_docs_md.py`
### `office-api/generate_docs_md.py`
This script generates Markdown documentation from the `apiBuilder.js` files.
- **Usage**:
```bash
python generate_docs_md.py output_path
```
- **Parameters**:
- `output_path` (optional): The directory where the Markdown documentation will be saved. If not specified, the default path is `../../../../office-js-api/`.
- `output_path` (optional): The directory where the Markdown documentation
will be saved. If not specified, the default path is
`../../../../office-js-api/`.
### `plugins/generate_docs_methods_json.py`
This script generates JSON documentation based on the `api_plugins.js` files.
- **Usage**:
```bash
python generate_docs_methods_json.py output_path
```
- **Parameters**:
- `output_path` (optional): The directory where the JSON documentation
will be saved. If not specified, the default path is
`../../../../office-js-api-declarations/office-js-api-plugins`.
### `plugins/generate_docs_events_json.py`
This script generates JSON documentation based on the `plugin-events.js` files.
- **Usage**:
```bash
python generate_docs_events_json.py output_path
```
- **Parameters**:
- `output_path` (optional): The directory where the JSON documentation
will be saved. If not specified, the default path is
`../../../../office-js-api-declarations/office-js-api-plugins`.
### `plugins/generate_docs_methods_md.py`
This script generates Markdown documentation from the `api_plugins.js` files.
- **Usage**:
```bash
python generate_docs_methods_md.py output_path
```
- **Parameters**:
- `output_path` (optional): The directory where the Markdown documentation
will be saved. If not specified, the default path is
`../../../../office-js-api/`.
### `plugins/generate_docs_events_md.py`
This script generates Markdown documentation from the `plugin-events.js` files.
- **Usage**:
```bash
python generate_docs_events_md.py output_path
```
- **Parameters**:
- `output_path` (optional): The directory where the Markdown documentation
will be saved. If not specified, the default path is
`../../../../office-js-api/`.
## Example
To generate JSON documentation with the default output path:
```bash
python generate_docs_json.py /path/to/save/json
```
To generate JSON documentation with the default output path:
```bash
python generate_docs_plugins_json.py /path/to/save/json
```
To generate Markdown documentation and specify a custom output path:
```bash
python generate_docs_md.py /path/to/save/markdown
```
## Notes
- Make sure to have all necessary permissions to run these scripts and write to the specified directories.
- Make sure to have all necessary permissions to run these scripts and write
to the specified directories.
- The output directories will be created if they do not exist.

View File

@ -1,16 +0,0 @@
{
"source": {
"include": ["../../../../sdkjs/word/apiBuilder.js", "../../../../sdkjs-forms/apiBuilder.js"]
},
"plugins": ["./correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -1,16 +0,0 @@
{
"source": {
"include": ["../../../../sdkjs/word/apiBuilder.js", "../../../../sdkjs/slide/apiBuilder.js"]
},
"plugins": ["./correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -1,409 +0,0 @@
import os
import json
import re
import shutil
import argparse
import generate_docs_json
# Configuration files
editors = [
"word",
"cell",
"slide",
"forms"
]
missing_examples = []
def load_json(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
def write_markdown_file(file_path, content):
with open(file_path, 'w', encoding='utf-8') as md_file:
md_file.write(content)
def remove_js_comments(text):
text = re.sub(r'^\s*//.*$', '', text, flags=re.MULTILINE) # single-line
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL) # multi-line
return text.strip()
def correct_description(string):
"""
Cleans up or transforms certain tags in a doclet description:
- <b> => **
- <note>...</note> => 💡 ...
- Provide a default if None.
"""
if string is None:
return 'No description provided.'
# Replace <b> tags with markdown bold
string = re.sub(r'<b>', '**', string)
string = re.sub(r'</b>', '**', string)
# Convert <note>...</note> to a little icon + text
string = re.sub(r'<note>(.*?)</note>', r'💡 \1', string, flags=re.DOTALL)
return string
def correct_default_value(value, enumerations, classes):
if value is None:
return ''
if value == True:
value = "true"
elif value == False:
value = "false"
else:
value = str(value)
return generate_data_types_markdown([value], enumerations, classes)
def remove_line_breaks(string):
return re.sub(r'[\r\n]+', ' ', string)
# Convert Array.<T> => T[] (including nested arrays).
def convert_jsdoc_array_to_ts(type_str: str) -> str:
"""
Recursively replaces 'Array.<T>' with 'T[]',
handling nested arrays like 'Array.<Array.<string>>' => 'string[][]'.
"""
pattern = re.compile(r'Array\.<([^>]+)>')
while True:
match = pattern.search(type_str)
if not match:
break
inner_type = match.group(1).strip()
# Recursively convert inner parts
inner_type = convert_jsdoc_array_to_ts(inner_type)
# Replace the outer Array.<...> with ...[]
type_str = (
type_str[:match.start()]
+ f"{inner_type}[]"
+ type_str[match.end():]
)
return type_str
def escape_text_outside_code_blocks(markdown: str) -> str:
"""
Splits content by fenced code blocks, escapes MDX-unsafe characters
(<, >, {, }) only in the text outside those code blocks.
"""
# A regex to capture fenced code blocks with ```
parts = re.split(r'(```.*?```)', markdown, flags=re.DOTALL)
# Even indices (0, 2, 4, ...) are outside code blocks,
# odd indices (1, 3, 5, ...) are actual code blocks.
for i in range(0, len(parts), 2):
# Only escape in parts outside code blocks
parts[i] = (parts[i]
.replace('<', '&lt;')
.replace('>', '&gt;')
.replace('{', '&#123;')
.replace('}', '&#125;')
)
return "".join(parts)
def get_base_type(ts_type: str) -> str:
"""
Given a TypeScript-like type (e.g. "Drawing[][]"), return the
'base' portion by stripping trailing "[]". For "Drawing[][]",
returns "Drawing". For "Array.<Drawing>", you'd convert it first
to "Drawing[]" then return "Drawing".
"""
while ts_type.endswith('[]'):
ts_type = ts_type[:-2]
return ts_type
def generate_data_types_markdown(types, enumerations, classes, root='../../'):
"""
1) Convert each raw JSDoc type from Array.<T> to T[].
2) Split union types if needed (usually they're provided as separate
elements in 'types' already, but let's be safe).
3) For each type, extract the base type (e.g. "Drawing" from "Drawing[]").
4) If the base type matches an enumeration or class, link the entire
T[]-based string.
5) Join with " | ".
"""
# Convert each raw type from JSDoc to TS
converted = [convert_jsdoc_array_to_ts(t) for t in types] # e.g. ["Drawing[]", "Foo[]", ...]
# For each converted type (like "Drawing[]"), see if the base is in enumerations or classes
def link_if_known(ts_type):
base = get_base_type(ts_type) # e.g. "Drawing" from "Drawing[]"
# Check enumerations first
for enum in enumerations:
if enum['name'] == base:
# Replace the entire token with a link
return f"[{ts_type}]({root}Enumeration/{base}.md)"
# Check classes
if base in classes:
return f"[{ts_type}]({root}{base}/{base}.md)"
# Otherwise just return as-is
return ts_type
# Build final list of possibly-linked types
linked = [link_if_known(ts_t) for ts_t in converted]
# Join them with " | "
param_types_md = r' \| '.join(linked)
# If there's still leftover angle brackets for generics, gently escape or link them
# e.g. "Object.<string, number>" => "Object.&lt;string, number&gt;"
# or do more specialized linking if you want to handle them deeper.
def replace_leftover_generics(match):
element = match.group(1).strip()
return f"&lt;{element}&gt;"
param_types_md = re.sub(r'<([^<>]+)>', replace_leftover_generics, param_types_md)
return param_types_md
def generate_class_markdown(class_name, methods, properties, enumerations, classes):
content = f"# {class_name}\n\nRepresents the {class_name} class.\n\n"
content += generate_properties_markdown(properties, enumerations, classes)
content += "## Methods\n\n"
for method in methods:
method_name = method['name']
content += f"- [{method_name}](./Methods/{method_name}.md)\n"
# Escape just before returning
return escape_text_outside_code_blocks(content)
def generate_method_markdown(method, enumerations, classes, example_editor_name):
method_name = method['name']
description = method.get('description', 'No description provided.')
description = correct_description(description)
params = method.get('params', [])
returns = method.get('returns', [])
example = method.get('example', '')
memberof = method.get('memberof', '')
content = f"# {method_name}\n\n{description}\n\n"
# Syntax
param_list = ', '.join([param['name'] for param in params]) if params else ''
content += f"## Syntax\n\n```javascript\nexpression.{method_name}({param_list});\n```\n\n"
if memberof:
content += f"`expression` - A variable that represents a [{memberof}](../{memberof}.md) class.\n\n"
# Parameters
content += "## Parameters\n\n"
if params:
content += "| **Name** | **Required/Optional** | **Data type** | **Default** | **Description** |\n"
content += "| ------------- | ------------- | ------------- | ------------- | ------------- |\n"
for param in params:
param_name = param.get('name', 'Unnamed')
param_types = param.get('type', {}).get('names', []) if param.get('type') else []
param_types_md = generate_data_types_markdown(param_types, enumerations, classes)
param_desc = remove_line_breaks(correct_description(param.get('description', 'No description provided.')))
param_required = "Required" if not param.get('optional') else "Optional"
param_default = correct_default_value(param.get('defaultvalue', ''), enumerations, classes)
content += f"| {param_name} | {param_required} | {param_types_md} | {param_default} | {param_desc} |\n"
else:
content += "This method doesn't have any parameters.\n"
# Returns
content += "\n## Returns\n\n"
if returns:
return_type_list = returns[0].get('type', {}).get('names', [])
return_type_md = generate_data_types_markdown(return_type_list, enumerations, classes)
content += return_type_md
else:
content += "This method doesn't return any data."
# Example
if example:
# Separate comment and code, remove JS comments
if '```js' in example:
comment, code = example.split('```js', 1)
comment = remove_js_comments(comment)
content += f"\n\n## Example\n\n{comment}\n\n```javascript {example_editor_name}\n{code.strip()}\n"
else:
# If there's no triple-backtick structure, just show it as code
cleaned_example = remove_js_comments(example)
content += f"\n\n## Example\n\n```javascript {example_editor_name}\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def generate_properties_markdown(properties, enumerations, classes, root='../'):
if properties is None:
return ''
content = "## Properties\n\n"
content += "| Name | Type | Description |\n"
content += "| ---- | ---- | ----------- |\n"
for prop in properties:
prop_name = prop['name']
prop_description = prop.get('description', 'No description provided.')
prop_description = remove_line_breaks(correct_description(prop_description))
prop_types = prop['type']['names'] if prop.get('type') else []
param_types_md = generate_data_types_markdown(prop_types, enumerations, classes, root)
content += f"| {prop_name} | {param_types_md} | {prop_description} |\n"
# Escape outside code blocks
return escape_text_outside_code_blocks(content)
def generate_enumeration_markdown(enumeration, enumerations, classes, example_editor_name):
enum_name = enumeration['name']
description = enumeration.get('description', 'No description provided.')
description = correct_description(description)
example = enumeration.get('example', '')
content = f"# {enum_name}\n\n{description}\n\n"
ptype = enumeration['type']['parsedType']
if ptype['type'] == 'TypeUnion':
enum_empty = True # is empty enum
content += "## Type\n\nEnumeration\n\n"
content += "## Values\n\n"
# Each top-level name in the union
for raw_t in enumeration['type']['names']:
ts_t = convert_jsdoc_array_to_ts(raw_t)
# Attempt linking: we compare the raw type to enumerations/classes
if any(enum['name'] == raw_t for enum in enumerations):
content += f"- [{ts_t}](../Enumeration/{raw_t}.md)\n"
enum_empty = False
elif raw_t in classes:
content += f"- [{ts_t}](../{raw_t}/{raw_t}.md)\n"
enum_empty = False
elif ts_t.find('Api') == -1:
content += f"- {ts_t}\n"
enum_empty = False
if enum_empty == True:
return None
elif enumeration['properties'] is not None:
content += "## Type\n\nObject\n\n"
content += generate_properties_markdown(enumeration['properties'], enumerations, classes)
else:
content += "## Type\n\n"
# If it's not a union and has no properties, simply print the type(s).
types = enumeration['type']['names']
t_md = generate_data_types_markdown(types, enumerations, classes)
content += t_md + "\n\n"
# Example
if example:
if '```js' in example:
comment, code = example.split('```js', 1)
comment = remove_js_comments(comment)
content += f"\n\n## Example\n\n{comment}\n\n```javascript {example_editor_name}\n{code.strip()}\n"
else:
# If there's no triple-backtick structure
cleaned_example = remove_js_comments(example)
content += f"\n\n## Example\n\n```javascript {example_editor_name}\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def process_doclets(data, output_dir, editor_name):
classes = {}
classes_props = {}
enumerations = []
editor_dir = os.path.join(output_dir, editor_name)
example_editor_name = 'editor-'
if editor_name == 'Word':
example_editor_name += 'docx'
elif editor_name == 'Forms':
example_editor_name += 'pdf'
elif editor_name == 'Slide':
example_editor_name += 'pptx'
elif editor_name == 'Cell':
example_editor_name += 'xlsx'
for doclet in data:
if doclet['kind'] == 'class':
class_name = doclet['name']
classes[class_name] = []
classes_props[class_name] = doclet.get('properties', None)
elif doclet['kind'] == 'function':
class_name = doclet.get('memberof')
if class_name:
if class_name not in classes:
classes[class_name] = []
classes[class_name].append(doclet)
elif doclet['kind'] == 'typedef':
enumerations.append(doclet)
# Process classes
for class_name, methods in classes.items():
class_dir = os.path.join(editor_dir, class_name)
methods_dir = os.path.join(class_dir, 'Methods')
os.makedirs(methods_dir, exist_ok=True)
# Write class file
class_content = generate_class_markdown(
class_name,
methods,
classes_props[class_name],
enumerations,
classes
)
write_markdown_file(os.path.join(class_dir, f"{class_name}.md"), class_content)
# Write method files
for method in methods:
method_file_path = os.path.join(methods_dir, f"{method['name']}.md")
method_content = generate_method_markdown(method, enumerations, classes, example_editor_name)
write_markdown_file(method_file_path, method_content)
if not method.get('example', ''):
missing_examples.append(os.path.relpath(method_file_path, output_dir))
# Process enumerations
enum_dir = os.path.join(editor_dir, 'Enumeration')
os.makedirs(enum_dir, exist_ok=True)
for enum in enumerations:
enum_file_path = os.path.join(enum_dir, f"{enum['name']}.md")
enum_content = generate_enumeration_markdown(enum, enumerations, classes, example_editor_name)
if enum_content is None:
continue
write_markdown_file(enum_file_path, enum_content)
if not enum.get('example', ''):
missing_examples.append(os.path.relpath(enum_file_path, output_dir))
def generate(output_dir):
print('Generating Markdown documentation...')
generate_docs_json.generate(output_dir + 'tmp_json', md=True)
for editor_name in editors:
input_file = os.path.join(output_dir + 'tmp_json', editor_name + ".json")
shutil.rmtree(output_dir + f'/{editor_name.title()}')
os.makedirs(output_dir + f'/{editor_name.title()}')
data = load_json(input_file)
process_doclets(data, output_dir, editor_name.title())
shutil.rmtree(output_dir + 'tmp_json')
print('Done')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate documentation")
parser.add_argument(
"destination",
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default="../../../../office-js-api/" # Default value
)
args = parser.parse_args()
generate(args.destination)
print("START_MISSING_EXAMPLES")
print(",".join(missing_examples))
print("END_MISSING_EXAMPLES")

View File

@ -1,467 +0,0 @@
import os
import json
import re
import shutil
import argparse
import generate_docs_plugins_json
# Configuration files
editors = [
"word",
"cell",
"slide",
"forms"
]
missing_examples = []
def load_json(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
def write_markdown_file(file_path, content):
with open(file_path, 'w', encoding='utf-8') as md_file:
md_file.write(content)
def remove_js_comments(text):
text = re.sub(r'^\s*//.*$', '', text, flags=re.MULTILINE) # single-line
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL) # multi-line
return text.strip()
def correct_description(string):
"""
Cleans up or transforms certain tags in a doclet description:
- <b> => **
- <note>...</note> => 💡 ...
- Provide a default if None.
"""
if string is None:
return 'No description provided.'
# Replace <b> tags with markdown bold
string = re.sub(r'<b>', '**', string)
string = re.sub(r'</b>', '**', string)
# Convert <note>...</note> to a little icon + text
string = re.sub(r'<note>(.*?)</note>', r'💡 \1', string, flags=re.DOTALL)
return string
def correct_default_value(value, enumerations, classes):
if value is None:
return ''
if value == True:
value = "true"
elif value == False:
value = "false"
else:
value = str(value)
return generate_data_types_markdown([value], enumerations, classes)
def remove_line_breaks(string):
return re.sub(r'[\r\n]+', ' ', string)
# Convert Array.<T> => T[] (including nested arrays).
def convert_jsdoc_array_to_ts(type_str: str) -> str:
"""
Recursively replaces 'Array.<T>' with 'T[]',
handling nested arrays like 'Array.<Array.<string>>' => 'string[][]'.
"""
pattern = re.compile(r'Array\.<([^>]+)>')
while True:
match = pattern.search(type_str)
if not match:
break
inner_type = match.group(1).strip()
# Recursively convert inner parts
inner_type = convert_jsdoc_array_to_ts(inner_type)
# Replace the outer Array.<...> with ...[]
type_str = (
type_str[:match.start()]
+ f"{inner_type}[]"
+ type_str[match.end():]
)
return type_str
def escape_text_outside_code_blocks(markdown: str) -> str:
"""
Splits content by fenced code blocks, escapes MDX-unsafe characters
(<, >, {, }) only in the text outside those code blocks.
"""
# A regex to capture fenced code blocks with ```
parts = re.split(r'(```.*?```)', markdown, flags=re.DOTALL)
# Even indices (0, 2, 4, ...) are outside code blocks,
# odd indices (1, 3, 5, ...) are actual code blocks.
for i in range(0, len(parts), 2):
# Only escape in parts outside code blocks
parts[i] = (parts[i]
.replace('<', '&lt;')
.replace('>', '&gt;')
.replace('{', '&#123;')
.replace('}', '&#125;')
)
return "".join(parts)
def get_base_type(ts_type: str) -> str:
"""
Given a TypeScript-like type (e.g. "Drawing[][]"), return the
'base' portion by stripping trailing "[]". For "Drawing[][]",
returns "Drawing". For "Array.<Drawing>", you'd convert it first
to "Drawing[]" then return "Drawing".
"""
while ts_type.endswith('[]'):
ts_type = ts_type[:-2]
return ts_type
def generate_data_types_markdown(types, enumerations, classes, root='../../'):
"""
1) Convert each raw JSDoc type from Array.<T> to T[].
2) Split union types if needed (usually they're provided as separate
elements in 'types' already, but let's be safe).
3) For each type, extract the base type (e.g. "Drawing" from "Drawing[]").
4) If the base type matches an enumeration or class, link the entire
T[]-based string.
5) Join with " | ".
"""
# Convert each raw type from JSDoc to TS
converted = [convert_jsdoc_array_to_ts(t) for t in types] # e.g. ["Drawing[]", "Foo[]", ...]
# For each converted type (like "Drawing[]"), see if the base is in enumerations or classes
def link_if_known(ts_type):
base = get_base_type(ts_type) # e.g. "Drawing" from "Drawing[]"
# Check enumerations first
for enum in enumerations:
if enum['name'] == base:
# Replace the entire token with a link
return f"[{ts_type}]({root}Enumeration/{base}.md)"
# Check classes
if base in classes:
return f"[{ts_type}]({root}{base}/{base}.md)"
# Otherwise just return as-is
return ts_type
# Build final list of possibly-linked types
linked = [link_if_known(ts_t) for ts_t in converted]
# Join them with " | "
param_types_md = r' \| '.join(linked)
# If there's still leftover angle brackets for generics, gently escape or link them
# e.g. "Object.<string, number>" => "Object.&lt;string, number&gt;"
# or do more specialized linking if you want to handle them deeper.
def replace_leftover_generics(match):
element = match.group(1).strip()
return f"&lt;{element}&gt;"
param_types_md = re.sub(r'<([^<>]+)>', replace_leftover_generics, param_types_md)
return param_types_md
def generate_class_markdown(class_name, methods, properties, enumerations, classes):
content = f"# {class_name}\n\nRepresents the {class_name} class.\n\n"
content += generate_properties_markdown(properties, enumerations, classes)
content += "## Methods\n\n"
for method in methods:
method_name = method['name']
content += f"- [{method_name}](./Methods/{method_name}.md)\n"
# Escape just before returning
return escape_text_outside_code_blocks(content)
def generate_method_markdown(method, enumerations, classes, example_editor_name):
"""
Generates Markdown for a method doclet, relying only on `method['examples']`
(array of strings). Ignores any single `method['example']` field.
"""
method_name = method['name']
description = method.get('description', 'No description provided.')
description = correct_description(description)
params = method.get('params', [])
returns = method.get('returns', [])
memberof = method.get('memberof', '')
# Use the 'examples' array only
examples = method.get('examples', [])
content = f"# {method_name}\n\n{description}\n\n"
# Syntax
param_list = ', '.join([param['name'] for param in params]) if params else ''
content += f"## Syntax\n\n```javascript\nexpression.{method_name}({param_list});\n```\n\n"
if memberof:
content += f"`expression` - A variable that represents a [{memberof}](../{memberof}.md) class.\n\n"
# Parameters
content += "## Parameters\n\n"
if params:
content += "| **Name** | **Required/Optional** | **Data type** | **Default** | **Description** |\n"
content += "| ------------- | ------------- | ------------- | ------------- | ------------- |\n"
for param in params:
param_name = param.get('name', 'Unnamed')
param_types = param.get('type', {}).get('names', []) if param.get('type') else []
param_types_md = generate_data_types_markdown(param_types, enumerations, classes)
param_desc = remove_line_breaks(correct_description(param.get('description', 'No description provided.')))
param_required = "Required" if not param.get('optional') else "Optional"
param_default = correct_default_value(param.get('defaultvalue', ''), enumerations, classes)
content += f"| {param_name} | {param_required} | {param_types_md} | {param_default} | {param_desc} |\n"
else:
content += "This method doesn't have any parameters.\n"
# Returns
content += "\n## Returns\n\n"
if returns:
return_type_list = returns[0].get('type', {}).get('names', [])
return_type_md = generate_data_types_markdown(return_type_list, enumerations, classes)
content += return_type_md
else:
content += "This method doesn't return any data."
# Process examples array
if examples:
if len(examples) > 1:
content += "\n\n## Examples\n\n"
else:
content += "\n\n## Example\n\n"
for i, ex_line in enumerate(examples, start=1):
# Remove JS comments
cleaned_example = remove_js_comments(ex_line).strip()
# Attempt splitting if the user used ```js
if '```js' in cleaned_example:
comment, code = cleaned_example.split('```js', 1)
comment = comment.strip()
code = code.strip()
if len(examples) > 1:
content += f"**Example {i}:**\n\n{comment}\n\n"
content += f"```javascript {example_editor_name}\n{code}\n```\n"
else:
if len(examples) > 1:
content += f"**Example {i}:**\n\n{comment}\n\n"
# No special fences, just show as code
content += f"```javascript {example_editor_name}\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def generate_properties_markdown(properties, enumerations, classes, root='../'):
if properties is None:
return ''
content = "## Properties\n\n"
content += "| Name | Type | Description |\n"
content += "| ---- | ---- | ----------- |\n"
for prop in properties:
prop_name = prop['name']
prop_description = prop.get('description', 'No description provided.')
prop_description = remove_line_breaks(correct_description(prop_description))
prop_types = prop['type']['names'] if prop.get('type') else []
param_types_md = generate_data_types_markdown(prop_types, enumerations, classes, root)
content += f"| {prop_name} | {param_types_md} | {prop_description} |\n"
# Escape outside code blocks
return escape_text_outside_code_blocks(content)
def generate_enumeration_markdown(enumeration, enumerations, classes, example_editor_name):
"""
Generates Markdown documentation for a 'typedef' doclet.
This version only works with `enumeration['examples']` (an array of strings),
ignoring any single `enumeration['examples']` field.
"""
enum_name = enumeration['name']
description = enumeration.get('description', 'No description provided.')
description = correct_description(description)
# Only use the 'examples' array
examples = enumeration.get('examples', [])
content = f"# {enum_name}\n\n{description}\n\n"
parsed_type = enumeration['type'].get('parsedType')
if not parsed_type:
# If parsedType is missing, just list 'type.names' if available
type_names = enumeration['type'].get('names', [])
if type_names:
content += "## Type\n\n"
t_md = generate_data_types_markdown(type_names, enumerations, classes)
content += t_md + "\n\n"
else:
ptype = parsed_type['type']
# 1) Handle TypeUnion
if ptype == 'TypeUnion':
content += "## Type\n\nEnumeration\n\n"
content += "## Values\n\n"
for raw_t in enumeration['type']['names']:
# Attempt linking
if any(enum['name'] == raw_t for enum in enumerations):
content += f"- [{raw_t}](../Enumeration/{raw_t}.md)\n"
elif raw_t in classes:
content += f"- [{raw_t}](../{raw_t}/{raw_t}.md)\n"
else:
content += f"- {raw_t}\n"
# 2) Handle TypeApplication (e.g. Object.<string, string>)
elif ptype == 'TypeApplication':
content += "## Type\n\nObject\n\n"
type_names = enumeration['type'].get('names', [])
if type_names:
t_md = generate_data_types_markdown(type_names, enumerations, classes)
content += f"**Type:** {t_md}\n\n"
# 3) If properties are present, treat it like an object
if enumeration.get('properties') is not None:
content += generate_properties_markdown(enumeration['properties'], enumerations, classes)
# 4) If it's neither TypeUnion nor TypeApplication, just output the type names
if ptype not in ('TypeUnion', 'TypeApplication'):
type_names = enumeration['type'].get('names', [])
if type_names:
content += "## Type\n\n"
t_md = generate_data_types_markdown(type_names, enumerations, classes)
content += t_md + "\n\n"
# Process examples array
if examples:
if len(examples) > 1:
content += "\n\n## Examples\n\n"
else:
content += "\n\n## Example\n\n"
for i, ex_line in enumerate(examples, start=1):
# Remove JS comments
cleaned_example = remove_js_comments(ex_line).strip()
# Attempt splitting if the user used ```js
if '```js' in cleaned_example:
comment, code = cleaned_example.split('```js', 1)
comment = comment.strip()
code = code.strip()
if len(examples) > 1:
content += f"**Example {i}:**\n\n{comment}\n\n"
content += f"```javascript {example_editor_name}\n{code}\n```\n"
else:
if len(examples) > 1:
content += f"**Example {i}:**\n\n{comment}\n\n"
# No special fences, just show as code
content += f"```javascript {example_editor_name}\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def process_doclets(data, output_dir, editor_name):
classes = {}
classes_props = {}
enumerations = []
editor_dir = os.path.join(output_dir, editor_name)
example_editor_name = 'editor-'
if editor_name == 'Word':
example_editor_name += 'docx'
elif editor_name == 'Forms':
example_editor_name += 'pdf'
elif editor_name == 'Slide':
example_editor_name += 'pptx'
elif editor_name == 'Cell':
example_editor_name += 'xlsx'
for doclet in data:
if doclet['kind'] == 'class':
class_name = doclet['name']
classes[class_name] = []
classes_props[class_name] = doclet.get('properties', None)
elif doclet['kind'] == 'function':
class_name = doclet.get('memberof')
if class_name:
if class_name not in classes:
classes[class_name] = []
classes[class_name].append(doclet)
elif doclet['kind'] == 'typedef':
enumerations.append(doclet)
# Process classes
for class_name, methods in classes.items():
class_dir = os.path.join(editor_dir, class_name)
methods_dir = os.path.join(class_dir, 'Methods')
os.makedirs(methods_dir, exist_ok=True)
# Write class file
class_content = generate_class_markdown(
class_name,
methods,
classes_props[class_name],
enumerations,
classes
)
write_markdown_file(os.path.join(class_dir, f"{class_name}.md"), class_content)
# Write method files
for method in methods:
method_file_path = os.path.join(methods_dir, f"{method['name']}.md")
method_content = generate_method_markdown(method, enumerations, classes, example_editor_name)
write_markdown_file(method_file_path, method_content)
if not method.get('examples', ''):
missing_examples.append(os.path.relpath(method_file_path, output_dir))
# Process enumerations
enum_dir = os.path.join(editor_dir, 'Enumeration')
os.makedirs(enum_dir, exist_ok=True)
for enum in enumerations:
enum_file_path = os.path.join(enum_dir, f"{enum['name']}.md")
enum_content = generate_enumeration_markdown(enum, enumerations, classes, example_editor_name)
if enum_content is None:
continue
write_markdown_file(enum_file_path, enum_content)
if not enum.get('examples', ''):
missing_examples.append(os.path.relpath(enum_file_path, output_dir))
def generate(output_dir):
print('Generating Markdown documentation...')
if output_dir[-1] == '/':
output_dir = output_dir[:-1]
generate_docs_plugins_json.generate(output_dir + '/tmp_json', md=True)
for editor_name in editors:
input_file = os.path.join(output_dir + '/tmp_json', editor_name + ".json")
shutil.rmtree(output_dir + f'/{editor_name.title()}', ignore_errors=True)
os.makedirs(output_dir + f'/{editor_name.title()}')
data = load_json(input_file)
process_doclets(data, output_dir, editor_name.title())
shutil.rmtree(output_dir + '/tmp_json')
print('Done')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate documentation")
parser.add_argument(
"destination",
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default="../../../../office-js-api/Plugins/" # Default value
)
args = parser.parse_args()
generate(args.destination)
print("START_MISSING_EXAMPLES")
print(",".join(missing_examples))
print("END_MISSING_EXAMPLES")

View File

@ -1,6 +1,6 @@
{
"source": {
"include": ["../../../../sdkjs/word/apiBuilder.js", "../../../../sdkjs/slide/apiBuilder.js", "../../../../sdkjs/cell/apiBuilder.js"]
"include": ["../../../../../sdkjs/word/apiBuilder.js", "../../../../../sdkjs/slide/apiBuilder.js", "../../../../../sdkjs/cell/apiBuilder.js"]
},
"plugins": ["./correct_doclets.js"],
"opts": {

View File

@ -66,11 +66,21 @@ exports.handlers = {
const isMethod = doclet.kind === 'function' || doclet.kind === 'method';
const hasTypeofEditorsTag = isMethod && doclet.tags && doclet.tags.some(tag => tag.title === 'typeofeditors' && tag.value.includes(process.env.EDITOR));
const shouldAddMethod =
let shouldAddMethod =
doclet.kind !== 'member' &&
(!doclet.longname || doclet.longname.search('private') === -1) &&
doclet.scope !== 'inner' && hasTypeofEditorsTag;
// class names may be the same between editors, we check against the inheritance tree
if (doclet.inherits) {
const parentClass = doclet.inherits.split('#')[0];
const curClass = cleanName(doclet.memberof);
if (!classesDocletsMap[curClass].augments || !classesDocletsMap[curClass].augments.includes(parentClass)) {
shouldAddMethod = false;
}
}
if (shouldAddMethod) {
// if the class is not in our map, then we deleted it ourselves -> not available in the editor
if (false == passedClasses.includes(cleanName(doclet.memberof))) {
@ -82,6 +92,11 @@ exports.handlers = {
doclet.longname = cleanName(doclet.longname);
doclet.name = cleanName(doclet.name);
// skip inherited methods if ovveriden in child class
if (doclet.inherited && filteredDoclets.find((addedDoclet) => addedDoclet['name'] == doclet['name'] && addedDoclet['memberof'] == doclet['memberof'])) {
continue;
}
const filteredDoclet = {
comment: doclet.comment,
description: doclet.description,

View File

@ -1,6 +1,6 @@
{
"source": {
"include": ["../../../../sdkjs-forms/apiPlugins.js"]
"include": ["../../../../../sdkjs/word/apiBuilder.js", "../../../../../sdkjs-forms/apiBuilder.js"]
},
"plugins": ["./correct_doclets.js"],
"opts": {

View File

@ -1,6 +1,6 @@
{
"source": {
"include": ["../../../../sdkjs/cell/api_plugins.js"]
"include": ["../../../../../sdkjs/word/apiBuilder.js", "../../../../../sdkjs/pdf/apiBuilder.js"]
},
"plugins": ["./correct_doclets.js"],
"opts": {

View File

@ -1,6 +1,6 @@
{
"source": {
"include": ["../../../../sdkjs/slide/api_plugins.js"]
"include": ["../../../../../sdkjs/word/apiBuilder.js", "../../../../../sdkjs/slide/apiBuilder.js"]
},
"plugins": ["./correct_doclets.js"],
"opts": {

View File

@ -1,6 +1,6 @@
{
"source": {
"include": ["../../../../sdkjs/word/apiBuilder.js"]
"include": ["../../../../../sdkjs/word/apiBuilder.js"]
},
"plugins": ["./correct_doclets.js"],
"opts": {

View File

@ -5,24 +5,29 @@ import argparse
import re
import platform
root = '../../../..'
script_path = os.path.abspath(__file__)
root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..'))
# Configuration files
configs = [
"./config/builder/word.json",
"./config/builder/cell.json",
"./config/builder/slide.json",
"./config/builder/forms.json"
"./config/word.json",
"./config/cell.json",
"./config/slide.json",
"./config/forms.json",
"./config/pdf.json"
]
editors_maps = {
"word": "CDE",
"cell": "CSE",
"slide": "CPE",
"forms": "CFE"
"forms": "CFE",
"pdf": "PDFE"
}
def generate(output_dir, md=False):
os.chdir(os.path.dirname(script_path))
if not os.path.exists(output_dir):
os.makedirs(output_dir)
@ -74,10 +79,7 @@ def generate(output_dir, md=False):
doclet['example'] = remove_js_comments(comment) + "```js\n" + code_content + "\n```"
if md == False:
document_type = editor_name
if "forms" == document_type:
document_type = "pdf"
doclet['description'] = doclet['description'] + f'\n\n## Try it\n\n ```js document-builder={{"documentType": "{document_type}"}}\n{code_content}\n```'
doclet['description'] = doclet['description'] + f'\n\n## Try it\n\n ```js document-builder={{"documentType": "{editor_name}"}}\n{code_content}\n```'
# Write the modified JSON file back
with open(output_file, 'w', encoding='utf-8') as f:

View File

@ -0,0 +1,669 @@
import os
import json
import re
import shutil
import argparse
import generate_docs_json
import json
from pathlib import PurePosixPath
# Configuration files
editors = {
"word": "text-document-api",
"cell": "spreadsheet-api",
"slide": "presentation-api",
"forms": "form-api",
"pdf": "pdf-api",
}
script_path = os.path.abspath(__file__)
root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..'))
missing_examples = []
used_enumerations = set()
translations = {}
translations_lang = None
missed_translations = {}
used_translations_keys = {}
global_output_dir = ""
cur_editor_name = None
def find_common_path_part(path_full: str, path_suffix: str, anchor: str) -> str:
path_full = path_full.replace('\\', '/')
path_suffix = path_suffix.replace('\\', '/')
parts1 = PurePosixPath(path_full).parts
parts2 = PurePosixPath(path_suffix).parts
try:
idx1 = [p.lower() for p in parts1].index(anchor.lower())
idx2 = [p.lower() for p in parts2].index(anchor.lower())
except ValueError:
return ""
common_segments = []
for p1, p2 in zip(parts1[idx1:], parts2[idx2:]):
if p1.lower() == p2.lower():
common_segments.append(p1)
else:
break
return "/".join(common_segments)
def load_json(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
def write_markdown_file(file_path, content):
with open(file_path, 'w', encoding='utf-8') as md_file:
md_file.write(content)
def remove_js_comments(text):
text = re.sub(r'^\s*//.*$', '', text, flags=re.MULTILINE) # single-line
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL) # multi-line
return text.strip()
def get_translation(key):
def process_part(k):
if k not in translations:
missed_translations[k] = k
else:
used_translations_keys[k] = True
return translations.get(k, k)
if '\\\n' in key:
parts = key.split('\\\n')
translated_parts = [process_part(p) for p in parts]
return '\\\n'.join(translated_parts)
return process_part(key)
def process_link_tags(text, root=''):
"""
Finds patterns like {@link ...} and replaces them with Markdown links.
If the prefix 'global#' is found, a link to a typedef is generated,
otherwise, a link to a class method is created.
For a method, if an alias is not specified, the name is left in the format 'Class#Method'.
"""
def replace_link(match):
content = match.group(1).strip() # Example: "global#ShapeType shape type" or "global#ErrorValue ErrorValue
parts = content.split()
ref = parts[0]
label = parts[1] if len(parts) > 1 else None
if ref.startswith('/docs/'):
url = root + '../../../..' + ref
display_text = label if label else ref
if url.endswith('/'):
last_dir = url.rstrip('/').split('/')[-1]
url = f"{url}{last_dir}"
return f"[{display_text}]({url}.md)"
elif ref.startswith("global#"):
# Handle links to typedef (similar logic as before)
typedef_name = ref.split("#")[1]
used_enumerations.add(typedef_name)
display_text = label if label else typedef_name
return f"[{display_text}]({root}Enumeration/{typedef_name}.md)"
else:
# Handle links to class methods like ClassName#MethodName
try:
class_name, method_name = ref.split("#")
except ValueError:
return match.group(0)
display_text = label if label else ref # Keep the full notation, e.g., "Api#CreateSlide"
return f"[{display_text}]({root}{class_name}/Methods/{method_name}.md)"
return re.sub(r'{@link\s+([^}]+)}', replace_link, text)
def correct_description(string, root='', isInTable=False):
"""
Cleans or transforms specific tags in the doclet description:
- <b> => ** (bold text)
- <note>...</note> => 💡 ...
- {@link ...} is replaced with a Markdown link
- If the description is missing, returns a default value.
- All '\r' characters are replaced with '\n'.
"""
if string is None:
return get_translation('No description provided.')
if False == isInTable:
# Line breaks
string = string.replace('\r', '\\\n')
# Replace <b> tags with Markdown bold formatting
string = re.sub(r'<b>', '-**', string)
else:
string = re.sub(r'<b>', '**', string)
string = remove_line_breaks(string)
string = re.sub(r'</b>', '**', string)
# Replace <note> tags with an icon and text
string = re.sub(r'<note>(.*?)</note>', r'💡 \1', string, flags=re.DOTALL)
# Process {@link ...} constructions
string = process_link_tags(string, root)
return get_translation(string)
def correct_default_value(value, enumerations, classes):
if value is None or value == '':
return ''
if isinstance(value, bool):
value = "true" if value else "false"
else:
value = str(value)
return generate_data_types_markdown([value], enumerations, classes)
def remove_line_breaks(string):
return re.sub(r'[\r\n]+', ' ', string)
# Convert Array.<T> => T[] (including nested arrays).
def convert_jsdoc_array_to_ts(type_str: str) -> str:
"""
Recursively replaces 'Array.<T>' with 'T[]',
handling nested arrays like 'Array.<Array.<string>>' => 'string[][]'.
"""
pattern = re.compile(r'Array\.<([^>]+)>')
while True:
match = pattern.search(type_str)
if not match:
break
inner_type = match.group(1).strip()
# Recursively convert inner parts
inner_type = convert_jsdoc_array_to_ts(inner_type)
# Replace the outer Array.<...> with ...[]
type_str = (
type_str[:match.start()]
+ f"{inner_type}[]"
+ type_str[match.end():]
)
return type_str
def escape_text_outside_code_blocks(markdown: str) -> str:
"""
Splits content by fenced code blocks, escapes MDX-unsafe characters
(<, >, {, }) only in the text outside those code blocks.
"""
# A regex to capture fenced code blocks with ```
parts = re.split(r'(```.*?```)', markdown, flags=re.DOTALL)
# Even indices (0, 2, 4, ...) are outside code blocks,
# odd indices (1, 3, 5, ...) are actual code blocks.
for i in range(0, len(parts), 2):
text = (parts[i]
.replace('<', '&lt;')
.replace('>', '&gt;')
.replace('{', '&#123;')
.replace('}', '&#125;'))
parts[i] = escape_brackets_in_quotes(text)
return "".join(parts)
def escape_brackets_in_quotes(text: str) -> str:
return re.sub(
r"(['\"])(.*?)(?<!\\)\1",
lambda m: m.group(1)
+ m.group(2).replace('[', r'\[').replace(']', r'\]')
+ m.group(1),
text
)
def get_base_type(ts_type: str) -> str:
"""
Given a TypeScript-like type (e.g. "Drawing[][]"), return the
'base' portion by stripping trailing "[]". For "Drawing[][]",
returns "Drawing". For "Array.<Drawing>", you'd convert it first
to "Drawing[]" then return "Drawing".
"""
while ts_type.endswith('[]'):
ts_type = ts_type[:-2]
return ts_type
def generate_data_types_markdown(types, enumerations, classes, root='../../'):
"""
1) Converts each type from JSDoc (e.g., Array.<T>) to T[].
2) Processes union types by splitting them using '|'.
3) Supports multidimensional arrays, e.g., (string|ApiRange|number)[].
4) If the base type matches the name of an enumeration or class, generates a link.
5) The final types are joined using " | ".
"""
# Convert each type from JSDoc format to TypeScript format (e.g., T[])
converted = [convert_jsdoc_array_to_ts(t) for t in types]
# Set of primitive types
primitive_types = {"string", "number", "boolean", "null", "undefined", "any", "object", "false", "true", "json", "function", "date", "{}"}
def is_primitive(type):
if (type.lower() in primitive_types or
(type.startswith('"') and type.endswith('"')) or
(type.startswith("'") and type.endswith("'")) or
type.replace('.', '', 1).isdigit() or
(type.startswith('-') and type[1:].replace('.', '', 1).isdigit())):
return True
return False
def link_if_known(ts_type):
ts_type = ts_type.strip()
# Count the number of array dimensions, e.g., "[][]" has 2 dimensions
array_dims = 0
while ts_type.endswith("[]"):
array_dims += 1
ts_type = ts_type[:-2].strip()
# Process generic types, e.g., Object.<string, editorType>
if ".<" in ts_type and ts_type.endswith(">"):
import re
m = re.match(r'^(.*?)\.<(.*)>$', ts_type)
if m:
base_part = m.group(1).strip()
generic_args_str = m.group(2).strip()
# Process the base part of the type
found = False
for enum in enumerations:
if enum['name'] == base_part:
used_enumerations.add(base_part)
base_result = f"[{base_part}]({root}Enumeration/{base_part}.md)"
found = True
break
if not found:
if base_part in classes:
base_result = f"[{base_part}]({root}{base_part}/{base_part}.md)"
elif is_primitive(base_part):
base_result = base_part
elif cur_editor_name == "forms":
base_result = f"[{base_part}]({root}../text-document-api/{base_part}/{base_part}.md)"
else:
print(f"Unknown type encountered: {base_part}")
base_result = base_part
# Split the generic parameters by commas and process each recursively
generic_args = [link_if_known(x) for x in generic_args_str.split(",")]
result = base_result + ".&lt;" + ", ".join(generic_args) + "&gt;"
result += "[]" * array_dims
return result
# Process union types: if the type is enclosed in parentheses
if ts_type.startswith("(") and ts_type.endswith(")"):
inner = ts_type[1:-1].strip()
subtypes = [sub.strip() for sub in inner.split("|")]
if len(subtypes) == 1:
result = link_if_known(subtypes[0])
else:
processed = [link_if_known(subtype) for subtype in subtypes]
result = "(" + " | ".join(processed) + ")"
result += "[]" * array_dims
return result
# If not a generic or union type process the base type
else:
base = ts_type
found = False
for enum in enumerations:
if enum['name'] == base:
used_enumerations.add(base)
result = f"[{base}]({root}Enumeration/{base}.md)"
found = True
break
if not found:
if base in classes:
result = f"[{base}]({root}{base}/{base}.md)"
elif is_primitive(base):
result = base
elif cur_editor_name == "forms":
result = f"[{base}]({root}../text-document-api/{base}/{base}.md)"
else:
print(f"Unknown type encountered: {base}")
result = base
result += "[]" * array_dims
return result
# Apply link_if_known to each converted type
linked = [link_if_known(ts_t) for ts_t in converted]
# Join results using " | "
param_types_md = r' | '.join(linked)
param_types_md = param_types_md.replace("|", r"\|")
# Escape remaining angle brackets for generics
def replace_leftover_generics(match):
element = match.group(1).strip()
return f"&lt;{element}&gt;"
param_types_md = re.sub(r'<([^<>]+)>', replace_leftover_generics, param_types_md)
return param_types_md
def generate_class_markdown(class_name, methods, properties, enumerations, classes):
content = f"# {class_name}\n\n{get_translation(f"Represents the {class_name} class.")}\n\n"
content += generate_properties_markdown(properties, enumerations, classes)
content += f"\n## {get_translation(f"Methods")}\n\n"
content += f"| {get_translation(f"Method")} | {get_translation(f"Returns")} | {get_translation(f"Description")} |\n"
content += "| ------ | ------- | ----------- |\n"
for method in sorted(methods, key=lambda m: m['name']):
method_name = method['name']
# Get the type of return values
returns = method.get('returns', [])
if returns:
return_type_list = returns[0].get('type', {}).get('names', [])
returns_markdown = generate_data_types_markdown(return_type_list, enumerations, classes, '../')
else:
returns_markdown = get_translation(f"None")
# Processing the method description
description = correct_description(method.get('description', 'No description provided.'), '../', True)
# Form a link to the method document
method_link = f"[{method_name}](./Methods/{method_name}.md)"
content += f"| {method_link} | {returns_markdown} | {description} |\n"
return escape_text_outside_code_blocks(content)
def generate_method_markdown(method, enumerations, classes, example_editor_name):
method_name = method['name']
description = method.get('description', 'No description provided.')
description = correct_description(description, '../../')
params = method.get('params', [])
returns = method.get('returns', [])
example = method.get('example', '')
memberof = method.get('memberof', '')
content = f"# {method_name}\n\n{description}\n\n"
# Syntax
param_list = ', '.join([param['name'] for param in params if '.' not in param['name']]) if params else ''
content += f"## {get_translation(f"Syntax")}\n\n```javascript\nexpression.{method_name}({param_list});\n```\n\n"
if memberof:
content += f"`expression` - {get_translation(f"A variable that represents a [{memberof}](../{memberof}.md) class.")}\n\n"
# Parameters
content += f"## {get_translation(f"Parameters")}\n\n"
if params:
content += f"| **{get_translation(f"Name")}** | **{get_translation(f"Required/Optional")}** | **{get_translation(f"Data type")}** | **{get_translation(f"Default")}** | **{get_translation(f"Description")}** |\n"
content += "| ------------- | ------------- | ------------- | ------------- | ------------- |\n"
for param in params:
param_name = param.get('name', 'Unnamed')
param_types = param.get('type', {}).get('names', []) if param.get('type') else []
param_types_md = generate_data_types_markdown(param_types, enumerations, classes)
param_desc = correct_description(param.get('description', 'No description provided.'), '../../', True)
param_required = f"{get_translation(f"Required")}" if not param.get('optional') else f"{get_translation(f"Optional")}"
param_default = correct_default_value(param.get('defaultvalue', ''), enumerations, classes)
content += f"| {param_name} | {param_required} | {param_types_md} | {param_default} | {param_desc} |\n"
else:
content += f"{get_translation("This method doesn't have any parameters.")}\n"
# Returns
content += f"\n## {get_translation(f"Returns")}\n\n"
if returns:
return_type_list = returns[0].get('type', {}).get('names', [])
return_type_md = generate_data_types_markdown(return_type_list, enumerations, classes)
content += return_type_md
else:
content += get_translation(f"This method doesn't return any data.")
# Example
if example:
# Separate comment and code, remove JS comments
if '```js' in example:
comment, code = example.split('```js', 1)
comment = get_translation(comment.strip())
content += f"\n\n## {get_translation(f"Example")}\n\n{comment}\n\n```javascript {example_editor_name}\n{code.strip()}\n"
else:
# If there's no triple-backtick structure, just show it as code
cleaned_example = remove_js_comments(example)
content += f"\n\n## {get_translation(f"Example")}\n\n```javascript {example_editor_name}\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def generate_properties_markdown(properties, enumerations, classes, root='../'):
if properties is None:
return ''
content = f"## {get_translation(f"Properties")}\n\n"
content += f"| {get_translation(f"Name")} | {get_translation(f"Type")} | {get_translation(f"Description")} |\n"
content += "| ---- | ---- | ----------- |\n"
for prop in sorted(properties, key=lambda m: m['name']):
prop_name = prop['name']
prop_description = prop.get('description', 'No description provided.')
prop_description = correct_description(prop_description, root, True)
prop_types = prop['type']['names'] if prop.get('type') else []
param_types_md = generate_data_types_markdown(prop_types, enumerations, classes, root)
content += f"| {prop_name} | {param_types_md} | {prop_description} |\n"
# Escape outside code blocks
return escape_text_outside_code_blocks(content)
def generate_enumeration_markdown(enumeration, enumerations, classes, example_editor_name):
enum_name = enumeration['name']
if enum_name not in used_enumerations:
return None
description = enumeration.get('description', 'No description provided.')
description = correct_description(description, '../')
example = enumeration.get('example', '')
content = f"# {enum_name}\n\n{description}\n\n"
ptype = enumeration['type']['parsedType']
if ptype['type'] == 'TypeUnion':
enum_empty = True # is empty enum
content += f"## {get_translation(f"Type")}\n\n{get_translation(f"Enumeration")}\n\n"
content += f"## {get_translation(f"Values")}\n\n"
# Each top-level name in the union
for raw_t in enumeration['type']['names']:
ts_t = convert_jsdoc_array_to_ts(raw_t)
# Attempt linking: we compare the raw type to enumerations/classes
if any(enum['name'] == raw_t for enum in enumerations):
used_enumerations.add(raw_t)
content += f"- [{ts_t}](../Enumeration/{raw_t}.md)\n"
enum_empty = False
elif raw_t in classes:
content += f"- [{ts_t}](../{raw_t}/{raw_t}.md)\n"
enum_empty = False
elif ts_t.find('Api') == -1:
content += f"- {ts_t}\n"
enum_empty = False
if enum_empty == True:
return None
elif enumeration['properties'] is not None:
content += f"## {get_translation(f"Type")}\n\n{get_translation(f"Object")}\n\n"
content += generate_properties_markdown(enumeration['properties'], enumerations, classes)
else:
content += f"## {get_translation(f"Type")}\n\n"
# If it's not a union and has no properties, simply print the type(s).
types = enumeration['type']['names']
t_md = generate_data_types_markdown(types, enumerations, classes, '../')
content += t_md + "\n\n"
# Example
if example:
if '```js' in example:
comment, code = example.split('```js', 1)
comment = get_translation(comment.strip())
content += f"\n\n## {get_translation(f"Example")}\n\n{comment}\n\n```javascript {example_editor_name}\n{code.strip()}\n"
else:
# If there's no triple-backtick structure
cleaned_example = remove_js_comments(example)
content += f"\n\n## {get_translation(f"Example")}\n\n```javascript {example_editor_name}\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def process_doclets(data, output_dir, editor_name):
global cur_editor_name
cur_editor_name = editor_name
classes = {}
classes_props = {}
enumerations = []
editor_dir = os.path.join(output_dir, editors[editor_name])
example_editor_name = 'editor-'
if editor_name == 'word':
example_editor_name += 'docx'
elif editor_name == 'forms':
example_editor_name += 'forms'
elif editor_name == 'slide':
example_editor_name += 'pptx'
elif editor_name == 'cell':
example_editor_name += 'xlsx'
elif editor_name == 'pdf':
example_editor_name += 'pdf'
for doclet in data:
if doclet['kind'] == 'class':
class_name = doclet['name']
if class_name:
if class_name not in classes:
classes[class_name] = []
classes_props[class_name] = doclet.get('properties', None)
elif doclet['kind'] == 'function':
class_name = doclet.get('memberof')
if class_name:
if class_name not in classes:
classes[class_name] = []
classes[class_name].append(doclet)
elif doclet['kind'] == 'typedef':
enumerations.append(doclet)
# Process classes
for class_name, methods in classes.items():
if (len(methods) == 0):
continue
class_dir = os.path.join(editor_dir, class_name)
methods_dir = os.path.join(class_dir, 'Methods')
os.makedirs(methods_dir, exist_ok=True)
# Write class file
class_content = generate_class_markdown(
class_name,
methods,
classes_props[class_name],
enumerations,
classes
)
write_markdown_file(os.path.join(class_dir, f"{class_name}.md"), class_content)
# Write method files
for method in methods:
method_file_path = os.path.join(methods_dir, f"{method['name']}.md")
method_content = generate_method_markdown(method, enumerations, classes, example_editor_name)
write_markdown_file(method_file_path, method_content)
if not method.get('example', ''):
missing_examples.append(os.path.relpath(method_file_path, output_dir))
# Process enumerations
enum_dir = os.path.join(editor_dir, 'Enumeration')
os.makedirs(enum_dir, exist_ok=True)
# idle run
prev_used_count = -1
while len(used_enumerations) != prev_used_count:
prev_used_count = len(used_enumerations)
for enum in [e for e in enumerations if e['name'] in used_enumerations]:
enum_content = generate_enumeration_markdown(enum, enumerations, classes, example_editor_name)
for enum in enumerations:
enum_file_path = os.path.join(enum_dir, f"{enum['name']}.md")
enum_content = generate_enumeration_markdown(enum, enumerations, classes, example_editor_name)
if enum_content is None:
continue
write_markdown_file(enum_file_path, enum_content)
if not enum.get('example', ''):
missing_examples.append(os.path.relpath(enum_file_path, output_dir))
def generate(output_dir, translations_file):
global translations
global translations_lang
global global_output_dir
global_output_dir = output_dir
if translations_file is not None and os.path.exists(translations_file):
translations = load_json(translations_file)
translations_lang = os.path.splitext(os.path.basename(translations_file))[0]
else:
translations = {}
os.chdir(os.path.dirname(script_path))
print('Generating Markdown documentation...')
generate_docs_json.generate(output_dir + 'tmp_json', md=True)
for editor_name, folder_name in editors.items():
input_file = os.path.join(output_dir + '/tmp_json', editor_name + ".json")
editor_folder_path = os.path.join(output_dir, folder_name)
for folder_name in os.listdir(editor_folder_path):
folder_path_to_del = os.path.join(editor_folder_path, folder_name)
if os.path.isdir(folder_path_to_del):
shutil.rmtree(folder_path_to_del, ignore_errors=True)
data = load_json(input_file)
used_enumerations.clear()
process_doclets(data, output_dir, editor_name)
if translations_file is not None:
target_dir = os.path.dirname(translations_file)
missed_file_path = os.path.join(target_dir, "missed_translations.json")
print(f'Saving missed translations to: {missed_file_path}')
with open(missed_file_path, 'w', encoding='utf-8') as f:
json.dump(missed_translations, f, ensure_ascii=False, indent=4)
unused_keys = set(translations.keys()) - set(used_translations_keys.keys())
unused_data = {k: translations[k] for k in unused_keys}
unused_file_path = os.path.join(target_dir, "unused_translations.json")
print(f'Saving unused translations to: {unused_file_path}')
with open(unused_file_path, 'w', encoding='utf-8') as f:
json.dump(unused_data, f, ensure_ascii=False, indent=4)
shutil.rmtree(output_dir + 'tmp_json')
print('Done')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate documentation")
parser.add_argument(
"destination",
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default=f"{root}/api.onlyoffice.com/site/docs/office-api/usage-api/" # Default value
)
parser.add_argument(
"--translations",
type=str,
help="Path to the JSON file with translations",
nargs='?',
default=None
)
args = parser.parse_args()
generate(args.destination, args.translations)
print("START_MISSING_EXAMPLES")
print(",".join(missing_examples))
print("END_MISSING_EXAMPLES")

View File

@ -11,10 +11,21 @@ editors = [
"word",
"cell",
"slide",
"forms"
"forms",
"pdf"
]
root = '../../../..'
editors_names = {
"word": "Word",
"cell": "Spreadsheet",
"slide": "Presentation",
"forms": "Forms",
"pdf": "PDF"
}
script_path = os.path.abspath(__file__)
root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..'))
missing_examples = []
def load_json(file_path):
@ -143,6 +154,8 @@ def create_entry(system_message, user_message, assistant_message, model):
return entry
def process_doclets(doclets, output_entries, editor_name, model):
system_message = f'You are an expert in API for library from OnlyOffice. The library provides functions for editing {editors_names[editor_name]} documents. Your functional capabilities: 1) Explanation of Onlyoffice JavaScript API classes and their methods and parameters. 2) Assistance in writing Onlyoffice JavaScript API examples upon user request. 3) Reviewing user examples, assisting in finding and fixing their mistakes.'
for doclet in doclets:
kind = doclet.get("kind", "").lower()
see = doclet.get("see", [])
@ -158,26 +171,14 @@ def process_doclets(doclets, output_entries, editor_name, model):
# Functions must have both "name" (method_name) and "memberof" fields filled
if not (method_name and memberof):
continue
system_message = (
f"You act as an API expert for Onlyoffice {editor_name.title()} editor. "
f"This task is an example for the function {method_name} in the class {memberof}."
)
default_user_message = f"How do I use the method {method_name} of {memberof} class?"
elif kind == "class":
class_name = doclet.get("name", "")
system_message = (
f"You act as an API expert for Onlyoffice {editor_name.title()} editor. "
f"This task is an example for the class {class_name}."
)
default_user_message = f"How do I instantiate or work with the class {class_name}?"
elif kind == "typedef":
typedef_name = doclet.get("name", "")
system_message = (
f"You act as an API expert for Onlyoffice {editor_name.title()} editor. "
f"This task is an example for the typedef {typedef_name}"
)
default_user_message = f"How do I use the typedef {typedef_name}?"
else:
@ -202,6 +203,8 @@ def process_doclets(doclets, output_entries, editor_name, model):
output_entries.append(create_entry(system_message, comment, assistant_message, model))
def generate(output_dir, model):
os.chdir(os.path.dirname(script_path))
print('Generating documentation JSONL dataset...')
shutil.rmtree(output_dir, ignore_errors=True)
@ -231,7 +234,7 @@ if __name__ == "__main__":
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default="../../../../office-js-api/dataset" # Default value
default=f"{root}/office-js-api/dataset" # Default value
)
parser.add_argument(
"model",

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs/cell/plugin-events.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs/common/base-plugin-events.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs-forms/plugin-events.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs/pdf/plugin-events.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs/slide/plugin-events.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs/word/plugin-events.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs/cell/api_plugins.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -1,8 +1,8 @@
{
"source": {
"include": ["../../../../sdkjs/common/plugins/plugin_base_api.js" ,"../../../../sdkjs/common/apiBase_plugins.js"]
"include": ["../../../../../sdkjs/common/plugins/plugin_base_api.js" ,"../../../../../sdkjs/common/apiBase_plugins.js"]
},
"plugins": ["./correct_doclets.js"],
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs-forms/apiPlugins.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs/pdf/api_plugins.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -0,0 +1,16 @@
{
"source": {
"include": ["../../../../../sdkjs/slide/api_plugins.js"]
},
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -1,8 +1,8 @@
{
"source": {
"include": ["../../../../sdkjs/word/api_plugins.js", "../../../../sdkjs-forms/apiPlugins.js"]
"include": ["../../../../../sdkjs/word/api_plugins.js", "../../../../../sdkjs-forms/apiPlugins.js"]
},
"plugins": ["./correct_doclets.js"],
"plugins": ["../correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,

View File

@ -6,16 +6,20 @@ import re
# Configuration files
configs = [
"./config/plugins/common.json",
"./config/plugins/word.json",
"./config/plugins/cell.json",
"./config/plugins/slide.json",
"./config/plugins/forms.json"
"./config/events/common.json",
"./config/events/word.json",
"./config/events/cell.json",
"./config/events/slide.json",
"./config/events/forms.json",
"./config/events/pdf.json"
]
root = '../../../..'
script_path = os.path.abspath(__file__)
root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..'))
def generate(output_dir, md=False):
os.chdir(os.path.dirname(script_path))
if not os.path.exists(output_dir):
os.makedirs(output_dir)
@ -49,7 +53,7 @@ def generate(output_dir, md=False):
# Modify JSON data
for idx, doclet in enumerate(data):
if idx == start_common_doclet_idx:
if idx >= start_common_doclet_idx:
example_folder_name = 'common'
elif editor_name == 'forms':
example_folder_name = 'word'
@ -84,7 +88,7 @@ def generate(output_dir, md=False):
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
print("Documentation generation for builder completed.")
print("Documentation generation for plugin events completed.")
def remove_builder_lines(text):
lines = text.splitlines() # Split text into lines

View File

@ -0,0 +1,474 @@
#!/usr/bin/env python3
import os
import json
import re
import shutil
import argparse
import generate_docs_events_json
import json
from pathlib import PurePosixPath
# Папки для каждого editor_name
editors = {
"word": "text-document-api",
"cell": "spreadsheet-api",
"slide": "presentation-api",
"forms": "form-api",
"pdf": "pdf-api"
}
script_path = os.path.abspath(__file__)
root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..'))
missing_examples = []
used_enumerations = set()
translations = {}
translations_lang = None
missed_translations = {}
used_translations_keys = {}
global_output_dir = ""
def find_common_path_part(path_full: str, path_suffix: str, anchor: str) -> str:
path_full = path_full.replace('\\', '/')
path_suffix = path_suffix.replace('\\', '/')
parts1 = PurePosixPath(path_full).parts
parts2 = PurePosixPath(path_suffix).parts
try:
idx1 = [p.lower() for p in parts1].index(anchor.lower())
idx2 = [p.lower() for p in parts2].index(anchor.lower())
except ValueError:
return ""
common_segments = []
for p1, p2 in zip(parts1[idx1:], parts2[idx2:]):
if p1.lower() == p2.lower():
common_segments.append(p1)
else:
break
return "/".join(common_segments)
def load_json(path):
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
def write_markdown_file(path, content):
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'w', encoding='utf-8') as f:
f.write(content)
def remove_js_comments(text):
text = re.sub(r'^\s*//.*$', '', text, flags=re.MULTILINE)
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
return text.strip()
def get_translation(key):
def process_part(k):
if k not in translations:
missed_translations[k] = k
else:
used_translations_keys[k] = True
return translations.get(k, k)
if '\\\n' in key:
parts = key.split('\\\n')
translated_parts = [process_part(p) for p in parts]
return '\\\n'.join(translated_parts)
return process_part(key)
def correct_description(string, root='', isInTable=False):
"""
Cleans or transforms specific tags in the doclet description:
- <b> => ** (bold text)
- <note>...</note> => 💡 ...
- {@link ...} is replaced with a Markdown link
- If the description is missing, returns a default value.
- All '\r' characters are replaced with '\n'.
"""
if string is None:
return get_translation('No description provided.')
if False == isInTable:
# Line breaks
string = string.replace('\r', '\\\n')
# Replace <b> tags with Markdown bold formatting
string = re.sub(r'<b>', '-**', string)
else:
string = re.sub(r'<b>', '**', string)
string = remove_line_breaks(string)
string = re.sub(r'</b>', '**', string)
# Replace <note> tags with an icon and text
string = re.sub(r'<note>(.*?)</note>', r'💡 \1', string, flags=re.DOTALL)
# Process {@link ...} constructions
string = process_link_tags(string, root)
return get_translation(string)
def process_link_tags(text, root=''):
"""
Finds patterns like {@link ...} and replaces them with Markdown links.
If the prefix 'global#' is found, a link to a typedef is generated,
otherwise, a link to a class method is created.
For a method, if an alias is not specified, the name is left in the format 'Class#Method'.
"""
def replace_link(match):
content = match.group(1).strip() # Example: "global#ShapeType shape type" or "global#ErrorValue ErrorValue
parts = content.split()
ref = parts[0]
label = parts[1] if len(parts) > 1 else None
if ref.startswith('/docs/'):
url = root + '../../../..' + ref
display_text = label if label else ref
if url.endswith('/'):
last_dir = url.rstrip('/').split('/')[-1]
url = f"{url}{last_dir}"
return f"[{display_text}]({url}.md)"
elif ref.startswith("global#"):
# Handle links to typedef (similar logic as before)
typedef_name = ref.split("#")[1]
used_enumerations.add(typedef_name)
display_text = label if label else typedef_name
return f"[{display_text}]({root}Enumeration/Event_{typedef_name}.md)"
else:
# Handle links to class methods like ClassName#MethodName
try:
class_name, method_name = ref.split("#")
except ValueError:
return match.group(0)
display_text = label if label else ref # Keep the full notation, e.g., "Api#CreateSlide"
return f"[{display_text}]({root}{class_name}/Methods/{method_name}.md)"
return re.sub(r'{@link\s+([^}]+)}', replace_link, text)
def remove_line_breaks(s):
return re.sub(r'[\r\n]+', ' ', s)
def convert_jsdoc_array_to_ts(type_str):
p = re.compile(r'Array\.<([^>]+)>')
while True:
m = p.search(type_str)
if not m:
break
inner = convert_jsdoc_array_to_ts(m.group(1).strip())
type_str = type_str[:m.start()] + inner + '[]' + type_str[m.end():]
return type_str
def generate_data_types_markdown(types, enumerations, root=''):
converted = [convert_jsdoc_array_to_ts(t) for t in types]
primitives = {"string", "number", "boolean", "null", "undefined", "any", "object", "false", "true", "json", "function", "date", "{}"}
result = []
enum_names = {e['name'] for e in enumerations}
for t in converted:
base = t.rstrip('[]')
dims = t[len(base):]
if base in enum_names:
used_enumerations.add(base)
link = f"[Event_{base}]({root}../Enumeration/Event_{base}.md)"
elif base in primitives or re.match(r"^['\"].*['\"]$", base) or re.match(r"^-?\d+(\.\d+)?$", base):
link = base
else:
link = base
result.append(link + dims)
return " | ".join(result)
def escape_text_outside_code_blocks(md):
parts = re.split(r'(```.*?```)', md, flags=re.DOTALL)
for i in range(0, len(parts), 2):
parts[i] = parts[i].replace('<', '&lt;').replace('>', '&gt;')
return "".join(parts)
def generate_event_markdown(event, enumerations):
name = event['name']
desc = correct_description(event.get('description', ''), '../', True)
params = event.get('params', [])
md = f"# {name}\n\n{desc}\n\n"
# Parameters
md += f"## {get_translation("Parameters")}\n\n"
if params:
md += f"| **{get_translation("Name")}** | **{get_translation("Data type")}** | **{get_translation("Description")}** |\n"
md += "| --------- | ------------- | ----------- |\n"
for p in params:
t_md = generate_data_types_markdown(
p.get('type', {}).get('names', []),
enumerations
)
d = correct_description(p.get('description', ''), isInTable=True)
md += f"| {p['name']} | {t_md} | {d} |\n"
md += "\n"
else:
md += f"{get_translation("This event has no parameters.")}\n\n"
for ex in event.get('examples', []):
code = remove_js_comments(ex).strip()
md += f"```javascript\n{code}\n```\n\n"
return escape_text_outside_code_blocks(md)
def generate_enumeration_markdown(enumeration, enumerations):
"""
Generates Markdown documentation for a 'typedef' doclet.
This version only works with `enumeration['examples']` (an array of strings),
ignoring any single `enumeration['examples']` field.
"""
enum_name = enumeration['name']
if enum_name not in used_enumerations:
return None
description = enumeration.get('description', 'No description provided.')
description = correct_description(description, '../')
# Only use the 'examples' array
examples = enumeration.get('examples', [])
content = f"# Event_{enum_name}\n\n{description}\n\n"
parsed_type = enumeration['type'].get('parsedType')
if not parsed_type:
# If parsedType is missing, just list 'type.names' if available
type_names = enumeration['type'].get('names', [])
if type_names:
content += f"## {get_translation("Type")}\n\n"
t_md = generate_data_types_markdown(type_names, enumerations)
content += t_md + "\n\n"
else:
ptype = parsed_type['type']
# 1) Handle TypeUnion
if ptype == 'TypeUnion':
content += f"## {get_translation("Type")}\n\n{get_translation("Enumeration")}\n\n"
content += f"## {get_translation("Values")}\n\n"
for raw_t in enumeration['type']['names']:
# Attempt linking
if any(enum['name'] == raw_t for enum in enumerations):
used_enumerations.add(raw_t)
content += f"- [{raw_t}](../Enumeration/Event_{raw_t}.md)\n"
else:
content += f"- {raw_t}\n"
# 2) Handle TypeApplication (e.g. Object.<string, string>)
elif ptype == 'TypeApplication':
content += f"## {get_translation("Type")}\n\n{get_translation("Object")}\n\n"
type_names = enumeration['type'].get('names', [])
if type_names:
t_md = generate_data_types_markdown(type_names, enumerations)
content += f"**{get_translation("Type")}:** {t_md}\n\n"
# 3) If properties are present, treat it like an object
if enumeration.get('properties') is not None:
content += generate_properties_markdown(enumeration['properties'], enumerations)
# 4) If it's neither TypeUnion nor TypeApplication, just output the type names
if ptype not in ('TypeUnion', 'TypeApplication'):
type_names = enumeration['type'].get('names', [])
if type_names:
content += f"## {get_translation("Type")}\n\n"
t_md = generate_data_types_markdown(type_names, enumerations)
content += t_md + "\n\n"
# Process examples array
if examples:
if len(examples) > 1:
content += f"\n\n## {get_translation("Examples")}\n\n"
else:
content += f"\n\n## {get_translation("Example")}\n\n"
for i, ex_line in enumerate(examples, start=1):
# Remove JS comments
cleaned_example = remove_js_comments(ex_line).strip()
# Attempt splitting if the user used ```js
if '```js' in cleaned_example:
comment, code = cleaned_example.split('```js', 1)
comment = get_translation(comment.strip())
code = code.strip()
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"```javascript\n{code}\n```\n"
else:
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
# No special fences, just show as code
content += f"```javascript\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def generate_events_summary(events):
"""
Create Events.md summary listing all events with their description.
"""
header = [
f"# {get_translation("Events")}\n\n",
f"| {get_translation("Event")} | {get_translation("Description")} |\n",
"| ----- | ----------- |\n"
]
lines = [
f"| [{ev['name']}](./{ev['name']}.md) | "
f"{correct_description(ev.get('description', ''), '../', isInTable=True)} |\n"
for ev in sorted(events, key=lambda e: e['name'])
]
return "".join(header + lines)
def generate_properties_markdown(properties, enumerations):
if properties is None:
return ''
content = f"## {get_translation("Properties")}\n\n"
content += f"| {get_translation("Name")} | {get_translation("Type")} | {get_translation("Description")} |\n"
content += "| ---- | ---- | ----------- |\n"
for prop in sorted(properties, key=lambda m: m['name']):
prop_name = prop['name']
prop_description = prop.get('description', 'No description provided.')
prop_description = correct_description(prop_description, isInTable=True)
prop_types = prop['type']['names'] if prop.get('type') else []
param_types_md = generate_data_types_markdown(prop_types, enumerations)
content += f"| {prop_name} | {param_types_md} | {prop_description} |\n"
# Escape outside code blocks
return escape_text_outside_code_blocks(content)
def clean_editor_dir(editor_dir):
for root, dirs, files in os.walk(editor_dir, topdown=False):
for file in files:
if not file.endswith(('.json')):
os.remove(os.path.join(root, file))
for dir in dirs:
dir_path = os.path.join(root, dir)
# remove empty folder
if not os.listdir(dir_path):
os.rmdir(dir_path)
def clean_enum_files(editor_dir: str):
for root, _, files in os.walk(editor_dir, topdown=False):
for file in files:
if True == file.startswith('Event_') and False == file.endswith('.json'):
os.remove(os.path.join(root, file))
def process_events(data, editor_dir):
enumerations = []
events = []
for doclet in data:
kind = doclet.get('kind')
if kind == 'typedef':
enumerations.append(doclet)
elif kind == 'event':
events.append(doclet)
events_dir = f'{editor_dir}/Events'
clean_editor_dir(events_dir)
os.makedirs(events_dir, exist_ok=True)
used_enumerations.clear()
# пишем события
for ev in events:
path = os.path.join(events_dir, f"{ev['name']}.md")
write_markdown_file(path, generate_event_markdown(ev, enumerations))
if not ev.get('examples'):
missing_examples.append(os.path.relpath(path, events_dir))
# пишем перечисления, используемые событиями
enum_dir = os.path.join(editor_dir, 'Enumeration')
clean_enum_files(enum_dir)
os.makedirs(enum_dir, exist_ok=True)
prev = -1
while len(used_enumerations) != prev:
prev = len(used_enumerations)
for e in enumerations:
if e['name'] in used_enumerations:
generate_enumeration_markdown(e, enumerations)
for e in enumerations:
if e['name'] in used_enumerations:
path = os.path.join(enum_dir, f"Event_{e['name']}.md")
write_markdown_file(path, generate_enumeration_markdown(e, enumerations))
if not e.get('examples'):
missing_examples.append(os.path.relpath(path, editor_dir))
# events summary
write_markdown_file(os.path.join(events_dir, "Events.md"), generate_events_summary(events))
def generate_events(output_dir, translations_file):
global translations
global translations_lang
global global_output_dir
global_output_dir = output_dir
if translations_file is not None and os.path.exists(translations_file):
translations = load_json(translations_file)
translations_lang = os.path.splitext(os.path.basename(translations_file))[0]
else:
translations = {}
os.chdir(os.path.dirname(script_path))
if output_dir.endswith('/'):
output_dir = output_dir[:-1]
tmp = os.path.join(output_dir, 'tmp_json')
generate_docs_events_json.generate(tmp, md=True)
for editor_name, folder in editors.items():
data = load_json(os.path.join(tmp, f"{editor_name}.json"))
process_events(data, os.path.join(output_dir, folder))
if translations_file is not None:
target_dir = os.path.dirname(translations_file)
missed_file_path = os.path.join(target_dir, "missed_translations.json")
print(f'Saving missed translations to: {missed_file_path}')
with open(missed_file_path, 'w', encoding='utf-8') as f:
json.dump(missed_translations, f, ensure_ascii=False, indent=4)
unused_keys = set(translations.keys()) - set(used_translations_keys.keys())
unused_data = {k: translations[k] for k in unused_keys}
unused_file_path = os.path.join(target_dir, "unused_translations.json")
print(f'Saving unused translations to: {unused_file_path}')
with open(unused_file_path, 'w', encoding='utf-8') as f:
json.dump(unused_data, f, ensure_ascii=False, indent=4)
shutil.rmtree(tmp)
print("Done. Missing examples:", missing_examples)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate events documentation")
parser.add_argument(
"destination",
nargs="?",
default=f"{root}/api.onlyoffice.com/site/docs/plugin-and-macros/interacting-with-editors/",
help="Output directory"
)
parser.add_argument(
"--translations",
type=str,
help="Path to the JSON file with translations",
nargs='?',
default=None
)
args = parser.parse_args()
generate_events(args.destination, args.translations)

View File

@ -0,0 +1,115 @@
import os
import subprocess
import json
import argparse
import re
# Configuration files
configs = [
"./config/methods/common.json",
"./config/methods/word.json",
"./config/methods/cell.json",
"./config/methods/slide.json",
"./config/methods/forms.json",
"./config/methods/pdf.json"
]
script_path = os.path.abspath(__file__)
root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..'))
def generate(output_dir, md=False):
os.chdir(os.path.dirname(script_path))
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# Generate JSON documentation
for config in configs:
editor_name = config.split('/')[-1].replace('.json', '')
output_file = os.path.join(output_dir, editor_name + ".json")
command = f"npx jsdoc -c {config} -X > {output_file}"
print(f"Generating {editor_name}.json: {command}")
subprocess.run(command, shell=True)
common_doclets_file = os.path.join(output_dir, 'common.json')
with open(common_doclets_file, 'r', encoding='utf-8') as f:
common_doclets_json = json.dumps(json.load(f))
os.remove(common_doclets_file)
# Append examples to JSON documentation
for config in configs:
if (config.find('common') != -1):
continue
editor_name = config.split('/')[-1].replace('.json', '')
example_folder_name = editor_name # name of folder with examples
output_file = os.path.join(output_dir, editor_name + ".json")
# Read the JSON file
with open(output_file, 'r', encoding='utf-8') as f:
data = json.load(f)
start_common_doclet_idx = len(data)
data += json.loads(common_doclets_json)
# Modify JSON data
for idx, doclet in enumerate(data):
if idx >= start_common_doclet_idx:
example_folder_name = 'common'
elif editor_name == 'forms':
example_folder_name = 'word'
if 'see' in doclet:
if doclet['see'] is not None:
doclet['see'][0] = doclet['see'][0].replace('{Editor}', example_folder_name.title())
file_path = f'{root}/' + doclet['see'][0]
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as see_file:
example_content = see_file.read()
# Extract the first line as a comment if it exists
lines = example_content.split('\n')
if lines[0].startswith('//'):
comment = lines[0] + '\n'
code_content = '\n'.join(lines[1:])
else:
comment = ''
code_content = example_content
doclet['examples'] = [remove_js_comments(comment) + code_content]
if md == False:
document_type = editor_name
if "forms" == document_type:
document_type = "pdf"
doclet['description'] = doclet['description'] + f'\n\n## Try it\n\n ```js document-builder={{"documentType": "{document_type}"}}\n{code_content}\n```'
# Write the modified JSON file back
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=4)
print("Documentation generation for plugin methods completed.")
def remove_builder_lines(text):
lines = text.splitlines() # Split text into lines
filtered_lines = [line for line in lines if not line.strip().startswith("builder.")]
return "\n".join(filtered_lines)
def remove_js_comments(text):
# Remove single-line comments, leaving text after //
text = re.sub(r'^\s*//\s?', '', text, flags=re.MULTILINE)
# Remove multi-line comments, leaving text after /*
text = re.sub(r'/\*\s*|\s*\*/', '', text, flags=re.DOTALL)
return text.strip()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate documentation")
parser.add_argument(
"destination",
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default=f"{root}/office-js-api-declarations/office-js-api-plugins"
)
args = parser.parse_args()
generate(args.destination)

View File

@ -0,0 +1,724 @@
import os
import json
import re
import shutil
import argparse
import generate_docs_methods_json
import json
from pathlib import PurePosixPath
# Configuration files
editors = {
"word": "text-document-api",
"cell": "spreadsheet-api",
"slide": "presentation-api",
"forms": "form-api",
"pdf": "pdf-api"
}
script_path = os.path.abspath(__file__)
root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..'))
missing_examples = []
used_enumerations = set()
translations = {}
translations_lang = None
missed_translations = {}
used_translations_keys = {}
global_output_dir = ""
cur_editor_name = None
def find_common_path_part(path_full: str, path_suffix: str, anchor: str) -> str:
path_full = path_full.replace('\\', '/')
path_suffix = path_suffix.replace('\\', '/')
parts1 = PurePosixPath(path_full).parts
parts2 = PurePosixPath(path_suffix).parts
try:
idx1 = [p.lower() for p in parts1].index(anchor.lower())
idx2 = [p.lower() for p in parts2].index(anchor.lower())
except ValueError:
return ""
common_segments = []
for p1, p2 in zip(parts1[idx1:], parts2[idx2:]):
if p1.lower() == p2.lower():
common_segments.append(p1)
else:
break
return "/".join(common_segments)
def load_json(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
def write_markdown_file(file_path, content):
with open(file_path, 'w', encoding='utf-8') as md_file:
md_file.write(content)
def remove_js_comments(text):
text = re.sub(r'^\s*//.*$', '', text, flags=re.MULTILINE) # single-line
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL) # multi-line
return text.strip()
def get_translation(key):
def process_part(k):
if k not in translations:
missed_translations[k] = k
else:
used_translations_keys[k] = True
return translations.get(k, k)
if '\\\n' in key:
parts = key.split('\\\n')
translated_parts = [process_part(p) for p in parts]
return '\\\n'.join(translated_parts)
return process_part(key)
def process_link_tags(text, root=''):
"""
Finds patterns like {@link ...} and replaces them with Markdown links.
If the prefix 'global#' is found, a link to a typedef is generated,
otherwise, a link to a class method is created.
For a method, if an alias is not specified, the name is left in the format 'Class#Method'.
"""
def replace_link(match):
content = match.group(1).strip() # Example: "global#ShapeType shape type" or "global#ErrorValue ErrorValue
parts = content.split()
ref = parts[0]
label = parts[1] if len(parts) > 1 else None
if ref.startswith('/docs/'):
url = root + '../../../..' + ref
display_text = label if label else ref
if url.endswith('/'):
last_dir = url.rstrip('/').split('/')[-1]
url = f"{url}{last_dir}"
return f"[{display_text}]({url}.md)"
elif ref.startswith("global#"):
# Handle links to typedef (similar logic as before)
typedef_name = ref.split("#")[1]
used_enumerations.add(typedef_name)
display_text = label if label else typedef_name
return f"[{display_text}]({root}Enumeration/{typedef_name}.md)"
else:
# Handle links to class methods like ClassName#MethodName
try:
class_name, method_name = ref.split("#")
except ValueError:
return match.group(0)
display_text = label if label else ref # Keep the full notation, e.g., "Api#CreateSlide"
return f"[{display_text}]({root}{class_name}/Methods/{method_name}.md)"
return re.sub(r'{@link\s+([^}]+)}', replace_link, text)
def correct_description(string, root='', isInTable=False):
"""
Cleans or transforms specific tags in the doclet description:
- <b> => ** (bold text)
- <note>...</note> => 💡 ...
- {@link ...} is replaced with a Markdown link
- If the description is missing, returns a default value.
- All '\r' characters are replaced with '\n'.
"""
if string is None:
return get_translation('No description provided.')
if False == isInTable:
# Line breaks
string = string.replace('\r', '\\\n')
# Replace <b> tags with Markdown bold formatting
string = re.sub(r'<b>', '-**', string)
else:
string = re.sub(r'<b>', '**', string)
string = remove_line_breaks(string)
string = re.sub(r'</b>', '**', string)
# Replace <note> tags with an icon and text
string = re.sub(r'<note>(.*?)</note>', r'💡 \1', string, flags=re.DOTALL)
# Process {@link ...} constructions
string = process_link_tags(string, root)
return get_translation(string)
def correct_default_value(value, enumerations, classes):
if value is None or value == '':
return ''
if value == True:
value = "true"
elif value == False:
value = "false"
else:
value = str(value)
return generate_data_types_markdown([value], enumerations, classes)
def remove_line_breaks(string):
return re.sub(r'[\r\n]+', ' ', string)
# Convert Array.<T> => T[] (including nested arrays).
def convert_jsdoc_array_to_ts(type_str: str) -> str:
"""
Recursively replaces 'Array.<T>' with 'T[]',
handling nested arrays like 'Array.<Array.<string>>' => 'string[][]'.
"""
pattern = re.compile(r'Array\.<([^>]+)>')
while True:
match = pattern.search(type_str)
if not match:
break
inner_type = match.group(1).strip()
# Recursively convert inner parts
inner_type = convert_jsdoc_array_to_ts(inner_type)
# Replace the outer Array.<...> with ...[]
type_str = (
type_str[:match.start()]
+ f"{inner_type}[]"
+ type_str[match.end():]
)
return type_str
def escape_text_outside_code_blocks(markdown: str) -> str:
"""
Splits content by fenced code blocks, escapes MDX-unsafe characters
(<, >, {, }) only in the text outside those code blocks.
"""
# A regex to capture fenced code blocks with ```
parts = re.split(r'(```.*?```)', markdown, flags=re.DOTALL)
# Even indices (0, 2, 4, ...) are outside code blocks,
# odd indices (1, 3, 5, ...) are actual code blocks.
for i in range(0, len(parts), 2):
text = (parts[i]
.replace('<', '&lt;')
.replace('>', '&gt;')
.replace('{', '&#123;')
.replace('}', '&#125;'))
parts[i] = escape_brackets_in_quotes(text)
return "".join(parts)
def escape_brackets_in_quotes(text: str) -> str:
return re.sub(
r"(['\"])(.*?)(?<!\\)\1",
lambda m: m.group(1)
+ m.group(2).replace('[', r'\[').replace(']', r'\]')
+ m.group(1),
text
)
def get_base_type(ts_type: str) -> str:
"""
Given a TypeScript-like type (e.g. "Drawing[][]"), return the
'base' portion by stripping trailing "[]". For "Drawing[][]",
returns "Drawing". For "Array.<Drawing>", you'd convert it first
to "Drawing[]" then return "Drawing".
"""
while ts_type.endswith('[]'):
ts_type = ts_type[:-2]
return ts_type
def generate_data_types_markdown(types, enumerations, classes, root='../'):
"""
1) Converts each type from JSDoc (e.g., Array.<T>) to T[].
2) Processes union types by splitting them using '|'.
3) Supports multidimensional arrays, e.g., (string|ApiRange|number)[].
4) If the base type matches the name of an enumeration or class, generates a link.
5) The final types are joined using " | ".
"""
# Convert each type from JSDoc format to TypeScript format (e.g., T[])
converted = [convert_jsdoc_array_to_ts(t) for t in types]
# Set of primitive types
primitive_types = {"string", "number", "boolean", "null", "undefined", "any", "object", "false", "true", "json", "function", "date", "{}"}
def is_primitive(type):
if (type.lower() in primitive_types or
(type.startswith('"') and type.endswith('"')) or
(type.startswith("'") and type.endswith("'")) or
type.replace('.', '', 1).isdigit() or
(type.startswith('-') and type[1:].replace('.', '', 1).isdigit())):
return True
return False
def link_if_known(ts_type):
ts_type = ts_type.strip()
# Count the number of array dimensions, e.g., "[][]" has 2 dimensions
array_dims = 0
while ts_type.endswith("[]"):
array_dims += 1
ts_type = ts_type[:-2].strip()
# Process generic types, e.g., Object.<string, editorType>
if ".<" in ts_type and ts_type.endswith(">"):
import re
m = re.match(r'^(.*?)\.<(.*)>$', ts_type)
if m:
base_part = m.group(1).strip()
generic_args_str = m.group(2).strip()
# Process the base part of the type
found = False
for enum in enumerations:
if enum['name'] == base_part:
used_enumerations.add(base_part)
base_result = f"[{base_part}]({root}Enumeration/{base_part}.md)"
found = True
break
if not found:
if base_part in classes:
base_result = f"[{base_part}]({root}{base_part}/{base_part}.md)"
elif is_primitive(base_part):
base_result = base_part
elif cur_editor_name == "forms":
base_result = f"[{base_part}]({root}../text-document-api/{base_part}/{base_part}.md)"
else:
print(f"Unknown type encountered: {base_part}")
base_result = base_part
# Split the generic parameters by commas and process each recursively
generic_args = [link_if_known(x) for x in generic_args_str.split(",")]
result = base_result + ".&lt;" + ", ".join(generic_args) + "&gt;"
result += "[]" * array_dims
return result
# Process union types: if the type is enclosed in parentheses
if ts_type.startswith("(") and ts_type.endswith(")"):
inner = ts_type[1:-1].strip()
subtypes = [sub.strip() for sub in inner.split("|")]
if len(subtypes) == 1:
result = link_if_known(subtypes[0])
else:
processed = [link_if_known(subtype) for subtype in subtypes]
result = "(" + " | ".join(processed) + ")"
result += "[]" * array_dims
return result
# If not a generic or union type process the base type
else:
base = ts_type
found = False
for enum in enumerations:
if enum['name'] == base:
used_enumerations.add(base)
result = f"[{base}]({root}Enumeration/{base}.md)"
found = True
break
if not found:
if base in classes:
result = f"[{base}]({root}{base}/{base}.md)"
elif is_primitive(base):
result = base
elif cur_editor_name == "forms":
result = f"[{base}]({root}../text-document-api/{base}/{base}.md)"
else:
print(f"Unknown type encountered: {base}")
result = base
result += "[]" * array_dims
return result
# Apply link_if_known to each converted type
linked = [link_if_known(ts_t) for ts_t in converted]
# Join results using " | "
param_types_md = r' | '.join(linked)
param_types_md = param_types_md.replace("|", r"\|")
# Escape remaining angle brackets for generics
def replace_leftover_generics(match):
element = match.group(1).strip()
return f"&lt;{element}&gt;"
param_types_md = re.sub(r'<([^<>]+)>', replace_leftover_generics, param_types_md)
return param_types_md
def generate_class_markdown(class_name, methods, properties, enumerations, classes):
content = f"# {class_name}\n\n{get_translation(f"Represents the {class_name} class.")}\n\n"
content += generate_properties_markdown(properties, enumerations, classes)
content += f"\n## {get_translation("Methods")}\n\n"
content += f"| {get_translation("Method")} | {get_translation("Returns")} | {get_translation("Description")} |\n"
content += "| ------ | ------- | ----------- |\n"
for method in sorted(methods, key=lambda m: m['name']):
method_name = method['name']
# Get the type of return values
returns = method.get('returns', [])
if returns:
return_type_list = returns[0].get('type', {}).get('names', [])
returns_markdown = generate_data_types_markdown(return_type_list, enumerations, classes, '../')
else:
returns_markdown = get_translation("None")
# Processing the method description
description = correct_description(method.get('description', 'No description provided.'), '../', True)
# Form a link to the method document
method_link = f"[{method_name}](./{method_name}.md)"
content += f"| {method_link} | {returns_markdown} | {description} |\n"
return escape_text_outside_code_blocks(content)
def generate_method_markdown(method, enumerations, classes):
"""
Generates Markdown for a method doclet, relying only on `method['examples']`
(array of strings). Ignores any single `method['example']` field.
"""
method_name = method['name']
description = method.get('description', 'No description provided.')
description = correct_description(description, '../')
params = method.get('params', [])
returns = method.get('returns', [])
memberof = method.get('memberof', '')
# Use the 'examples' array only
examples = method.get('examples', [])
content = f"# {method_name}\n\n{description}\n\n"
# Syntax
param_list = ', '.join([param['name'] for param in params if '.' not in param['name']]) if params else ''
content += f"## {get_translation("Syntax")}\n\n```javascript\nexpression.{method_name}({param_list});\n```\n\n"
if memberof:
content += f"`expression` - {get_translation(f"A variable that represents a [{memberof}](Methods.md) class.")}\n\n"
# Parameters
content += f"## {get_translation("Parameters")}\n\n"
if params:
content += f"| **{get_translation("Name")}** | **{get_translation("Required/Optional")}** | **{get_translation("Data type")}** | **{get_translation("Default")}** | **{get_translation("Description")}** |\n"
content += "| ------------- | ------------- | ------------- | ------------- | ------------- |\n"
for param in params:
param_name = param.get('name', 'Unnamed')
param_types = param.get('type', {}).get('names', []) if param.get('type') else []
param_types_md = generate_data_types_markdown(param_types, enumerations, classes)
param_desc = correct_description(param.get('description', 'No description provided.'), '../', True)
param_required = get_translation("Required") if not param.get('optional') else get_translation("Optional")
param_default = correct_default_value(param.get('defaultvalue', ''), enumerations, classes)
content += f"| {param_name} | {param_required} | {param_types_md} | {param_default} | {param_desc} |\n"
else:
content += f"{get_translation("This method doesn't have any parameters.")}\n"
# Returns
content += f"\n## {get_translation("Returns")}\n\n"
if returns:
return_type_list = returns[0].get('type', {}).get('names', [])
return_type_md = generate_data_types_markdown(return_type_list, enumerations, classes)
content += return_type_md
else:
content += get_translation("This method doesn't return any data.")
# Process examples array
if examples:
if len(examples) > 1:
content += f"\n\n## {get_translation("Examples")}\n\n"
else:
content += f"\n\n## {get_translation("Example")}\n\n"
for i, ex_line in enumerate(examples, start=1):
# Remove JS comments
cleaned_example = remove_js_comments(ex_line).strip()
# Attempt splitting if the user used ```js
if '```js' in cleaned_example:
comment, code = cleaned_example.split('```js', 1)
comment = get_translation(comment.strip())
code = code.strip()
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"```javascript\n{code}\n```\n"
else:
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
# No special fences, just show as code
content += f"```javascript\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def generate_properties_markdown(properties, enumerations, classes, root='../'):
if properties is None:
return ''
content = f"## {get_translation("Properties")}\n\n"
content += f"| {get_translation("Name")} | {get_translation("Type")} | {get_translation("Description")} |\n"
content += "| ---- | ---- | ----------- |\n"
for prop in sorted(properties, key=lambda m: m['name']):
prop_name = prop['name']
prop_description = prop.get('description', 'No description provided.')
prop_description = correct_description(prop_description, root, isInTable=True)
prop_types = prop['type']['names'] if prop.get('type') else []
param_types_md = generate_data_types_markdown(prop_types, enumerations, classes, root)
content += f"| {prop_name} | {param_types_md} | {prop_description} |\n"
# Escape outside code blocks
return escape_text_outside_code_blocks(content)
def generate_enumeration_markdown(enumeration, enumerations, classes):
"""
Generates Markdown documentation for a 'typedef' doclet.
This version only works with `enumeration['examples']` (an array of strings),
ignoring any single `enumeration['examples']` field.
"""
enum_name = enumeration['name']
if enum_name not in used_enumerations:
return None
description = enumeration.get('description', 'No description provided.')
description = correct_description(description, '../')
# Only use the 'examples' array
examples = enumeration.get('examples', [])
content = f"# {enum_name}\n\n{description}\n\n"
parsed_type = enumeration['type'].get('parsedType')
if not parsed_type:
# If parsedType is missing, just list 'type.names' if available
type_names = enumeration['type'].get('names', [])
if type_names:
content += f"## {get_translation("Type")}\n\n"
t_md = generate_data_types_markdown(type_names, enumerations, classes)
content += t_md + "\n\n"
else:
ptype = parsed_type['type']
# 1) Handle TypeUnion
if ptype == 'TypeUnion':
content += f"## {get_translation("Type")}\n\n{get_translation("Enumeration")}\n\n"
content += f"## {get_translation("Values")}\n\n"
for raw_t in enumeration['type']['names']:
# Attempt linking
if any(enum['name'] == raw_t for enum in enumerations):
used_enumerations.add(raw_t)
content += f"- [{raw_t}](../Enumeration/{raw_t}.md)\n"
elif raw_t in classes:
content += f"- [{raw_t}](../{raw_t}/{raw_t}.md)\n"
else:
content += f"- {raw_t}\n"
# 2) Handle TypeApplication (e.g. Object.<string, string>)
elif ptype == 'TypeApplication':
content += f"## {get_translation("Type")}\n\n{get_translation("Object")}\n\n"
type_names = enumeration['type'].get('names', [])
if type_names:
t_md = generate_data_types_markdown(type_names, enumerations, classes)
content += f"**{get_translation("Type")}:** {t_md}\n\n"
# 3) If properties are present, treat it like an object
if enumeration.get('properties') is not None:
content += generate_properties_markdown(enumeration['properties'], enumerations, classes)
# 4) If it's neither TypeUnion nor TypeApplication, just output the type names
if ptype not in ('TypeUnion', 'TypeApplication'):
type_names = enumeration['type'].get('names', [])
if type_names:
content += f"## {get_translation("Type")}\n\n"
t_md = generate_data_types_markdown(type_names, enumerations, classes)
content += t_md + "\n\n"
# Process examples array
if examples:
if len(examples) > 1:
content += f"\n\n## {get_translation("Examples")}\n\n"
else:
content += f"\n\n## {get_translation("Example")}\n\n"
for i, ex_line in enumerate(examples, start=1):
# Remove JS comments
cleaned_example = remove_js_comments(ex_line).strip()
# Attempt splitting if the user used ```js
if '```js' in cleaned_example:
comment, code = cleaned_example.split('```js', 1)
comment = get_translation(comment.strip())
code = code.strip()
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"```javascript\n{code}\n```\n"
else:
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
# No special fences, just show as code
content += f"```javascript\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
def clean_methods_dir(methods_dir):
for root, dirs, files in os.walk(methods_dir, topdown=False):
for file in files:
if not file.endswith(('.json')):
os.remove(os.path.join(root, file))
for dir in dirs:
dir_path = os.path.join(root, dir)
# remove empty folder
if not os.listdir(dir_path):
os.rmdir(dir_path)
def clean_enum_files(editor_dir: str):
for root, _, files in os.walk(editor_dir, topdown=False):
for file in files:
if False == file.startswith('Event_') and False == file.endswith('.json'):
os.remove(os.path.join(root, file))
def process_doclets(data, output_dir, editor_name):
global cur_editor_name
cur_editor_name = editor_name
classes = {}
classes_props = {}
enumerations = []
editor_dir = os.path.join(output_dir, editors[editor_name])
methods_dir = os.path.join(output_dir, editors[editor_name], 'Methods')
clean_methods_dir(methods_dir)
os.makedirs(methods_dir, exist_ok=True)
for doclet in data:
if doclet['kind'] == 'class':
class_name = doclet['name']
if class_name:
if class_name not in classes:
classes[class_name] = []
classes_props[class_name] = doclet.get('properties', None)
elif doclet['kind'] == 'function':
class_name = doclet.get('memberof')
if class_name:
if class_name not in classes:
classes[class_name] = []
classes[class_name].append(doclet)
elif doclet['kind'] == 'typedef':
enumerations.append(doclet)
# Process api methods
class_name = 'Api'
methods = classes[class_name]
# Write class file
class_content = generate_class_markdown(
class_name,
methods,
classes_props[class_name],
enumerations,
classes
)
write_markdown_file(os.path.join(methods_dir, f"Methods.md"), class_content)
# Write method files
for method in methods:
method_file_path = os.path.join(methods_dir, f"{method['name']}.md")
method_content = generate_method_markdown(method, enumerations, classes)
write_markdown_file(method_file_path, method_content)
if not method.get('examples', ''):
missing_examples.append(os.path.relpath(method_file_path, output_dir))
# Process enumerations
enum_dir = os.path.join(editor_dir, 'Enumeration')
clean_enum_files(enum_dir)
os.makedirs(enum_dir, exist_ok=True)
# idle run
prev_used_count = -1
while len(used_enumerations) != prev_used_count:
prev_used_count = len(used_enumerations)
for enum in [e for e in enumerations if e['name'] in used_enumerations]:
enum_content = generate_enumeration_markdown(enum, enumerations, classes)
for enum in enumerations:
enum_file_path = os.path.join(enum_dir, f"{enum['name']}.md")
enum_content = generate_enumeration_markdown(enum, enumerations, classes)
if enum_content is None:
continue
write_markdown_file(enum_file_path, enum_content)
if not enum.get('examples', ''):
missing_examples.append(os.path.relpath(enum_file_path, output_dir))
def generate(output_dir, translations_file):
global translations
global translations_lang
global global_output_dir
global_output_dir = output_dir
if translations_file is not None and os.path.exists(translations_file):
translations = load_json(translations_file)
translations_lang = os.path.splitext(os.path.basename(translations_file))[0]
else:
translations = {}
os.chdir(os.path.dirname(script_path))
print('Generating Markdown documentation...')
if output_dir[-1] == '/':
output_dir = output_dir[:-1]
generate_docs_methods_json.generate(output_dir + '/tmp_json', md=True)
for editor_name, folder_name in editors.items():
input_file = os.path.join(output_dir + '/tmp_json', editor_name + ".json")
data = load_json(input_file)
used_enumerations.clear()
process_doclets(data, output_dir, editor_name)
if translations_file is not None:
target_dir = os.path.dirname(translations_file)
missed_file_path = os.path.join(target_dir, "missed_translations.json")
print(f'Saving missed translations to: {missed_file_path}')
with open(missed_file_path, 'w', encoding='utf-8') as f:
json.dump(missed_translations, f, ensure_ascii=False, indent=4)
unused_keys = set(translations.keys()) - set(used_translations_keys.keys())
unused_data = {k: translations[k] for k in unused_keys}
unused_file_path = os.path.join(target_dir, "unused_translations.json")
print(f'Saving unused translations to: {unused_file_path}')
with open(unused_file_path, 'w', encoding='utf-8') as f:
json.dump(unused_data, f, ensure_ascii=False, indent=4)
shutil.rmtree(output_dir + '/tmp_json')
print('Done')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate documentation")
parser.add_argument(
"destination",
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default=f"{root}/api.onlyoffice.com/site/docs/plugin-and-macros/interacting-with-editors/" # Default value
)
parser.add_argument(
"--translations",
type=str,
help="Path to the JSON file with translations",
nargs='?',
default=None
)
args = parser.parse_args()
generate(args.destination, args.translations)
print("START_MISSING_EXAMPLES")
print(",".join(missing_examples))
print("END_MISSING_EXAMPLES")

View File

@ -133,7 +133,7 @@ def get_projects(pro_json_path, platform):
is_needed_platform_exist = False
for pl in platform_records:
if is_exist_in_array(params, pl):
is_needed_platform_exist = True;
is_needed_platform_exist = True
break
# if one config exists => all needed must exists
@ -144,7 +144,7 @@ def get_projects(pro_json_path, platform):
if is_exist_in_array(platform_records, item):
continue
is_needed_config_exist = True
break;
break
if is_needed_platform_exist:
if not is_exist_in_array(params, platform):

View File

@ -13,12 +13,15 @@
"core/UnicodeConverter/UnicodeConverter.pro",
"core/Common/kernel.pro",
"core/Common/Network/network.pro",
"core/OdfFile/Reader/Converter/StarMath2OOXML/StarMath2OOXML.pro",
"core/DesktopEditor/graphics/pro/graphics.pro",
"core/PdfFile/PdfFile.pro",
"core/DjVuFile/DjVuFile.pro",
"core/XpsFile/XpsFile.pro",
"core/OFDFile/OFDFile.pro",
"core/HtmlFile2/HtmlFile2.pro",
"core/Fb2File/Fb2File.pro",
"core/EpubFile/CEpubFile.pro",
@ -26,10 +29,12 @@
"core/Apple/IWork.pro",
"core/DocxRenderer/DocxRenderer.pro",
"core/DocxRenderer/DocxRenderer.pro",
"core/DesktopEditor/doctrenderer/doctrenderer.pro",
"core/DesktopEditor/xmlsec/src/ooxmlsignature.pro",
"[!no_x2t]core/OOXML/Projects/Linux/DocxFormatLib/DocxFormatLib.pro",
"[!no_x2t]core/OOXML/Projects/Linux/PPTXFormatLib/PPTXFormatLib.pro",
"[!no_x2t]core/OOXML/Projects/Linux/XlsbFormatLib/XlsbFormatLib.pro",
@ -78,22 +83,19 @@
"multimedia" : [
"[win,linux]desktop-sdk/ChromiumBasedEditors/videoplayerlib/videoplayerlib.pro"
],
"desktop" : [
"core",
"multimedia",
"core/DesktopEditor/xmlsec/src/ooxmlsignature.pro",
"desktop-sdk/ChromiumBasedEditors/lib/ascdocumentscore.pro",
"desktop-sdk/ChromiumBasedEditors/lib/ascdocumentscore_helper.pro",
"[win,linux]desktop-sdk/ChromiumBasedEditors/lib/qt_wrapper/qtascdocumentscore.pro",
"[win,linux]desktop-apps/win-linux/ASCDocumentEditor.pro",
"[win]desktop-apps/win-linux/extras/projicons/ProjIcons.pro",
"[win,!win_xp]desktop-apps/win-linux/extras/update-daemon/UpdateDaemon.pro",
"[win_xp]desktop-apps/win-linux/extras/online-installer/OnlineInstaller.pro"
"[win,!win_xp]desktop-apps/win-linux/extras/update-daemon/UpdateDaemon.pro"
],
"mobile" : [

View File

@ -68,6 +68,7 @@ AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_HTMLR = AVS_OFFICESTUDIO_FILE_CROS
AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_HTMLRMenu = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM + 0x0007
AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_HTMLRCanvas = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM + 0x0008
AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_PDFA = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM + 0x0009
AVS_OFFICESTUDIO_FILE_CROSSPLATFORM_OFD = AVS_OFFICESTUDIO_FILE_CROSSPLATFORM + 0x000a
AVS_OFFICESTUDIO_FILE_IMAGE = 0x0400
AVS_OFFICESTUDIO_FILE_IMAGE_JPG = AVS_OFFICESTUDIO_FILE_IMAGE + 0x0001

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
import sys
sys.path.append('../../scripts')
import base
import os
import glob
from pathlib import Path
params = sys.argv[1:]
if (3 != len(params)):
print("use: thumbnails_auto.py path_to_builder_directory path_to_input_files_directory path_to_output_files_directory")
exit(0)
base.configure_common_apps()
directory_x2t = params[0].replace("\\", "/")
directory_input = params[1].replace("\\", "/")
directory_output = params[2].replace("\\", "/")
if not os.path.exists(directory_output):
os.mkdir(directory_output)
def rename_dir(input, output):
if base.is_dir(u"" + directory_output + u"/" + output):
base.delete_dir(u"" + directory_output + u"/" + output)
os.rename(u"" + directory_output + u"/" + input, u"" + directory_output + u"/" + output)
return
base.cmd("python", ["thumbnails_old.py", directory_x2t, directory_input, directory_output, "512", "724"])
base.cmd("python", ["thumbnails_old.py", directory_x2t, directory_input, directory_output, "1024", "1448"])
base.cmd("python", ["thumbnails_old.py", directory_x2t, directory_input, directory_output, "324", "458"])
base.cmd("python", ["thumbnails_old.py", directory_x2t, directory_input, directory_output, "648", "916"])
base.cmd("python", ["thumbnails_old.py", directory_x2t, directory_input, directory_output, "256", "368"])
base.cmd("python", ["thumbnails_old.py", directory_x2t, directory_input, directory_output, "400", "566"])
base.cmd("python", ["thumbnails_old.py", directory_x2t, directory_input, directory_output, "184", "260"])
#base.cmd("python", ["thumbnails.py", directory_x2t, directory_input, directory_output, "792", "1098"])
#rename_dir("[512x724]", "inside_1x_[512x724]")
#rename_dir("[1024x1448]", "inside_2x_[1024x1448]")
#rename_dir("[228x316]", "main_1x_[228x316]")
#rename_dir("[456x632]", "main_2x_[456x632]")
#rename_dir("[256x368]", "mobile_[256x368]")
#rename_dir("[792x1098]", "source_[792x1098]")
dirnames = list(Path(directory_output).iterdir())
for dir_name in dirnames:
if len(list(Path(dir_name).iterdir())) == 0:
print("Delete dir ", dir_name)
Path(dir_name).rmdir()
#base.delete_dir(dir_name)

View File

@ -0,0 +1,274 @@
#!/usr/bin/env python
import sys
sys.path.append('../../scripts')
import base
import os
import glob
import imagesize
import importlib.util
from pathlib import Path
from os.path import dirname, abspath, join
# Python 3 compatibility hack
try:
unicode('')
except NameError:
unicode = str
params = sys.argv[1:]
if (5 != len(params)):
print("use: thumbnails.py path_to_builder_directory path_to_input_files_directory path_to_output_files_directory width height")
exit(0)
mapping = {"[512x724]": "inside_1x_[512x724]",
"[1024x1448]": "inside_2x_[1024x1448]",
"[228x316]": "main_1x_[228x316]",
"[456x632]": "main_2x_[456x632]",
"[256x368]": "mobile_[256x368]",
"[792x1098]": "source_[792x1098]",
"[324x458]": "main_1x_[324x458]",
"[648x916]": "main_2x_[648x916]",
"[400x566]": "pop_up_[400x566]",
"[184x260]": "desktop[184x260]",
}
cur_path = os.getcwd()
base.configure_common_apps()
directory_x2t = params[0].replace("\\", "/")
directory_input = params[1].replace("\\", "/")
directory_output = params[2].replace("\\", "/")
th_width = params[3]
th_height = params[4]
docbuilder_path = os.path.join(directory_x2t, "docbuilder.py")
if not os.path.isfile(docbuilder_path):
print(f"ERROR: docbuilder.py not found in '{directory_x2t}'")
exit(1)
spec = importlib.util.spec_from_file_location("docbuilder", docbuilder_path)
docbuilder_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(docbuilder_mod)
CDocBuilder = docbuilder_mod.CDocBuilder
CDocBuilderValue = docbuilder_mod.CDocBuilderValue
#output_dir = directory_output + "/[" + str(th_width) + "x" + str(th_height) + "]"
#if base.is_dir(output_dir):
# base.delete_dir(output_dir)
#base.create_dir(output_dir)
input_files = []
for file in glob.glob(os.path.join(u"" + directory_input, u'*')):
input_files.append(file.replace("\\", "/"))
#print(input_files)
temp_dir = os.getcwd().replace("\\", "/") + "/temp"
if base.is_dir(temp_dir):
base.delete_dir(temp_dir)
base.create_dir(temp_dir)
directory_fonts = directory_x2t + "/sdkjs/common"
if not base.is_file(directory_fonts + "/AllFonts.js"):
base.cmd_in_dir(directory_x2t, "docbuilder", [], True)
# # True for fit, False for 100%
# isScaleSheetToPage = False
#
# json_fit_text = "0"
# if isScaleSheetToPage:
# json_fit_text = "1"
#
# #json_params += "'fitToWidth':" + json_fit_text + ",'fitToHeight':" + json_fit_text + ","
# if isScaleSheetToPage:
# json_params = "{'spreadsheetLayout':{'fitToWidth':1,'fitToHeight':1},"
# else:
# json_params = "{'spreadsheetLayout':{'fitToWidth':0,'fitToHeight':0},"
# json_params += "'documentLayout':{'drawPlaceHolders':true,'drawFormHighlight':true,'isPrint':true}}"
# json_params = json_params.replace("'", "&quot;")
json_params = "{"
json_params += "'spreadsheetLayout':{"
# True for fit, False for 100%
isScaleSheetToPage = False
json_fit_text = "0"
if isScaleSheetToPage:
json_fit_text = "1"
json_params += "'fitToWidth':" + json_fit_text + ",'fitToHeight':" + json_fit_text + ","
if True:
json_params += "'orientation':'landscape',"
page_margins = "'pageMargins':{'bottom':10,'footer':5,'header':5,'left':5,'right':5,'top':10}"
page_setup = "'pageSetup':{'orientation':1,'width':210,'height':297,'paperUnits':0,'scale':190," \
"'printArea':false,'horizontalDpi':600,'verticalDpi':600,'usePrinterDefaults':true,'fitToHeight':1,'fitToWidth':1}"
json_params += "'sheetsProps':{'0':{'headings':false,'printTitlesWidth':null,'printTitlesHeight':null," + page_margins + "," + page_setup + "}}},"
json_params += "'documentLayout':{'drawPlaceHolders':true,'drawFormHighlight':true,'isPrint':true},"
json_params += "'ignorePrintArea':'false'"
json_params += "}"
json_params = json_params.replace("'", "&quot;")
if not os.path.exists(directory_output):
os.mkdir(directory_output)
output_len = len(input_files)
output_cur = 1
for input_file in input_files:
if os.path.isdir(input_file):
next_dir_name = os.path.basename(input_file)
base.cmd("python", ["thumbnails_old.py", directory_x2t, input_file, os.path.join(directory_output, next_dir_name), th_width, th_height])
if base.is_dir(temp_dir):
base.delete_dir(temp_dir)
base.create_dir(temp_dir)
continue
print("process [" + str(output_cur) + " of " + str(output_len) + "]: " + str(input_file.encode("utf-8")))
width_page = th_width
height_page = th_height
json_params_file = json_params
if input_file.lower().endswith('.xlsx'):
temp_dir_builder = directory_output + "/temp_builder"
if base.is_dir(temp_dir_builder):
base.delete_dir(temp_dir_builder)
base.create_dir(temp_dir_builder)
builder = CDocBuilder()
builder.SetTmpFolder(temp_dir_builder)
builder.OpenFile(input_file)
context = builder.GetContext()
globalObj = context.GetGlobal()
cmd = """
(function(){
Api.getPrintAreaSize = function() {
return { Width:0, Height:0 };
};
var sheet = Api.GetSheets()[0];
var usedRange = sheet.GetUsedRange();
var maxCol = -1;
var maxRow = -1;
usedRange.ForEach(function (cell) {
if (cell.GetRowHeight() === 0 || cell.GetColumnWidth() === 0) {
return;
}
var row0 = cell.GetRow() - 1;
var col0 = cell.GetCol() - 1;
var hasContent = false;
var val = cell.GetValue();
if (val !== "" && val !== null && val !== undefined) {
hasContent = true;
}
if (!hasContent) {
var formula = cell.GetFormula();
if (typeof formula === 'string' && formula.indexOf("=") === 0) {
hasContent = true;
}
}
if (hasContent) {
if (col0 > maxCol) maxCol = col0;
if (row0 > maxRow) maxRow = row0;
}
});
if (maxRow < 0 || maxCol < 0) {
return;
}
var printRange = sheet.GetRange(
sheet.GetRangeByNumber(0, 0),
sheet.GetRangeByNumber(maxRow, maxCol)
);
Api.getPrintAreaSize = function() {
return { Width:printRange.Width, Height:printRange.Height };
};})();
"""
builder.ExecuteCommand(cmd)
api = globalObj['Api']
sizeWH = api.getPrintAreaSize()
wPrint = sizeWH.Get("Width").ToDouble()
hPrint = sizeWH.Get("Height").ToDouble()
print("CELL (printSize): " + str(wPrint) + "x" + str(hPrint))
if (wPrint > 1 and hPrint > wPrint):
tmp = width_page
width_page = height_page
height_page = tmp
json_params_file = json_params_file.replace("&quot;width&quot;:210,&quot;height&quot;:297", "&quot;width&quot;:297,&quot;height&quot;:210")
builder.CloseFile()
base.delete_dir(temp_dir_builder)
output_dir = os.path.join(directory_output,
os.path.splitext(os.path.basename(input_file))[0])
#output_dir = str(output_dir.encode("utf8"))
output_dir = abspath(unicode(output_dir))
if not os.path.exists(output_dir):
os.mkdir(output_dir)
output_dir = os.path.join(output_dir,
mapping["[" + str(th_width) + "x" + str(th_height) + "]"])
#output_dir = str(output_dir.encode("utf8"))
#output_dir = dirname(abspath(unicode(output_dir)))
output_dir = abspath(unicode(output_dir))
if not os.path.exists(output_dir):
os.mkdir(output_dir)
output_file = output_dir # os.path.join(output_dir, os.path.splitext(os.path.basename(input_file))[0])
xml_convert = u"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
xml_convert += u"<TaskQueueDataConvert>"
xml_convert += (u"<m_sFileFrom>" + input_file + u"</m_sFileFrom>")
xml_convert += (u"<m_sFileTo>" + output_file + u".zip</m_sFileTo>")
xml_convert += u"<m_nFormatTo>1029</m_nFormatTo>"
xml_convert += (u"<m_sAllFontsPath>" + directory_fonts + u"/AllFonts.js</m_sAllFontsPath>")
xml_convert += (u"<m_sFontDir>" + directory_fonts + u"</m_sFontDir>")
xml_convert += (u"<m_sJsonParams>" + json_params_file + u"</m_sJsonParams>")
xml_convert += u"<m_nDoctParams>1</m_nDoctParams>"
xml_convert += u"<m_oThumbnail>"
xml_convert += u"<first>false</first>"
if ((0 != width_page) and (0 != height_page)):
xml_convert += u"<aspect>16</aspect>"
xml_convert += (u"<width>" + str(width_page) + u"</width>")
xml_convert += (u"<height>" + str(height_page) + u"</height>")
xml_convert += u"</m_oThumbnail>"
xml_convert += u"<m_nDoctParams>1</m_nDoctParams>"
xml_convert += (u"<m_sTempDir>" + temp_dir + u"</m_sTempDir>")
xml_convert += u"</TaskQueueDataConvert>"
base.save_as_script(temp_dir + "/to.xml", [xml_convert])
base.cmd_in_dir(directory_x2t, "x2t", [temp_dir + "/to.xml"], True)
base.delete_dir(temp_dir)
base.create_dir(temp_dir)
base.extract_unicode(output_file + u".zip", output_file)
if os.path.exists(output_file + ".zip"):
try:
base.delete_file(output_file + ".zip")
except:
print("Error in deletin file: ", output_file + ".zip")
output_cur += 1
#output_file = output_file.replace("\\", "/")
#imnames = Path(output_file).glob("*.png")#glob.glob("/" + output_file.replace(":", "") + "/*.png")
imnames = [str(pp) for pp in Path(output_file).glob("*.png")]
#print(output_file + "/*.png", imnames)
#continue
if len(imnames) == 0:
base.delete_dir(output_file)
else:
width, height = imagesize.get(imnames[0])
print("WxH: ", width, height)
if width < height and False: #удалить вертикальные превью
base.delete_dir(output_file)
base.delete_dir(temp_dir)
os.chdir(cur_path)

5
tools/linux/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
qt_build
packages_complete
python3
python3.tar.gz
extract.sh

View File

@ -0,0 +1,5 @@
qt_source*
qt_binary*
qt_build*
qt-*
gcc-linaro-*

View File

@ -0,0 +1,103 @@
#!/usr/bin/env python
import sys
import os
sys.path.append('../../../../scripts')
import base
def update_qmake_conf(arm_toolchain_bin):
replace_file = "./qt-everywhere-src-5.15.2/qtbase/mkspecs/linux-aarch64-gnu-g++/qmake.conf"
arm_toolchain_bin = os.path.abspath(arm_toolchain_bin)
replace_src = ""
replace_src += "# modifications to g++.conf\n"
replace_src += "QMAKE_CC = aarch64-linux-gnu-gcc\n"
replace_src += "QMAKE_CXX = aarch64-linux-gnu-g++\n"
replace_src += "QMAKE_LINK = aarch64-linux-gnu-g++\n"
replace_src += "QMAKE_LINK_SHLIB = aarch64-linux-gnu-g++\n"
replace_src += "\n"
replace_src += "# modifications to linux.conf\n"
replace_src += "QMAKE_AR = aarch64-linux-gnu-ar cqs\n"
replace_src += "QMAKE_OBJCOPY = aarch64-linux-gnu-objcopy\n"
replace_src += "QMAKE_NM = aarch64-linux-gnu-nm -P\n"
replace_src += "QMAKE_STRIP = aarch64-linux-gnu-strip\n"
replace_dst = ""
replace_dst += "# modifications to g++.conf\n"
replace_dst += "QMAKE_CC = " + arm_toolchain_bin + "/aarch64-linux-gnu-gcc\n"
replace_dst += "QMAKE_CXX = " + arm_toolchain_bin + "/aarch64-linux-gnu-g++\n"
replace_dst += "QMAKE_LINK = " + arm_toolchain_bin + "/aarch64-linux-gnu-g++\n"
replace_dst += "QMAKE_LINK_SHLIB = " + arm_toolchain_bin + "/aarch64-linux-gnu-g++\n"
replace_dst += "\n"
replace_dst += "# modifications to linux.conf\n"
replace_dst += "QMAKE_AR = " + arm_toolchain_bin + "/aarch64-linux-gnu-ar cqs\n"
replace_dst += "QMAKE_OBJCOPY = " + arm_toolchain_bin + "/aarch64-linux-gnu-objcopy\n"
replace_dst += "QMAKE_NM = " + arm_toolchain_bin + "/aarch64-linux-gnu-nm -P\n"
replace_dst += "QMAKE_STRIP = " + arm_toolchain_bin + "/aarch64-linux-gnu-strip\n"
base.replaceInFile(replace_file, replace_src, replace_dst)
def make(arm_toolchain_bin=""):
qt_build_path = os.path.dirname(os.path.abspath(__file__)) + "/qt_build/Qt-5.15.2/linux_arm64"
qt_params = ["-opensource",
"-confirm-license",
"-release",
"-shared",
"-accessibility",
"-prefix", "\"" + qt_build_path + "\"",
"-extprefix", "\"" + qt_build_path + "\"",
"-hostprefix", "\"" + qt_build_path + "\"",
"-c++std", "c++11",
"-qt-zlib",
"-qt-libpng",
"-qt-libjpeg",
"-qt-pcre",
"-no-sql-sqlite",
"-no-opengl",
"-nomake", "examples",
"-nomake", "tests",
"-skip", "qtlocation",
"-skip", "qtserialport",
"-skip", "qtsensors",
"-skip", "qtxmlpatterns",
"-skip", "qt3d",
"-skip", "qtwebview",
"-skip", "qtwebengine",
"-skip", "qtdeclarative",
"-xplatform", "linux-aarch64-gnu-g++", # be sure that aarch64 gnu compiler is installed
"-no-pch"]
qt_params_str = ""
for param in qt_params:
qt_params_str += (param + " ")
qt_url = "https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/qt/qt-everywhere-src-5.15.2.tar.xz"
if not base.is_file("./qt_source_5.15.2.tar.xz"):
base.download(qt_url, "./qt_source_5.15.2.tar.xz")
if not base.is_dir("./qt-everywhere-src-5.15.2"):
base.cmd("tar", ["-xf", "./qt_source_5.15.2.tar.xz"])
# https://bugreports.qt.io/browse/QTBUG-93452
# for GCC 11 and Qt5/Qt6
additional_gcc_11 = "#ifdef __cplusplus\n#include <limits>\n#endif\n"
chanage_file = "./qt-everywhere-src-5.15.2/qtbase/src/corelib/global/qglobal.h"
filedata = base.readFile(chanage_file)
if filedata.find(additional_gcc_11) == -1:
filedata = additional_gcc_11 + filedata
base.writeFile(chanage_file, filedata)
if arm_toolchain_bin != "":
update_qmake_conf(arm_toolchain_bin)
base.cmd_in_dir("./qt-everywhere-src-5.15.2", "./configure " + qt_params_str)
base.cmd_in_dir("./qt-everywhere-src-5.15.2", "make -j4")
base.cmd_in_dir("./qt-everywhere-src-5.15.2", "make install")
if __name__ == "__main__":
arm_toolchain_path = "./gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu/bin"
if len(sys.argv) != 1:
arm_toolchain_path = sys.argv[1]
make(arm_toolchain_path)

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python
import sys
import os
sys.path.append('../../../../scripts')
import base
def make():
arm_toolchain_url = 'https://releases.linaro.org/components/toolchain/binaries/5.4-2017.05/aarch64-linux-gnu/'
arm_toolchain_tar_filename = 'gcc-linaro-5.4.1-2017.05-x86_64_aarch64-linux-gnu.tar.xz'
base.cmd2('wget', [arm_toolchain_url + arm_toolchain_tar_filename])
base.cmd2('tar', ['-xf', arm_toolchain_tar_filename])
if __name__ == "__main__":
make()

View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
import sys
import os
sys.path.append('../../../scripts')
import base
def make():
qt_build_path = os.path.dirname(os.path.abspath(__file__)) + "/qt_build/Qt-5.15.2"
qt_binary_url = "https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/qt/qt_binary_linux_arm64.7z"
if not base.is_file("./qt_binary_linux_arm64.7z"):
base.download(qt_binary_url, "./qt_binary_linux_arm64.7z")
if not base.is_dir(qt_build_path):
os.makedirs(qt_build_path)
base.cmd("tar", ["-xf", "./qt_binary_linux_arm64.7z", "-C", qt_build_path])
if __name__ == "__main__":
make()

View File

@ -0,0 +1,10 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

View File

@ -0,0 +1,5 @@
{
"exclude": [
"qt-everywhere-src-5.15.2/**"
]
}

View File

@ -6,6 +6,7 @@ import base
import os
import subprocess
import deps
import qt_binary_build
def get_branch_name(directory):
cur_dir = os.getcwd()
@ -20,7 +21,7 @@ def get_branch_name(directory):
def install_qt():
# qt
if not base.is_file("./qt_source_5.9.9.tar.xz"):
base.download("https://download.qt.io/new_archive/qt/5.9/5.9.9/single/qt-everywhere-opensource-src-5.9.9.tar.xz", "./qt_source_5.9.9.tar.xz")
base.download("https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/qt/qt-everywhere-opensource-src-5.9.9.tar.xz", "./qt_source_5.9.9.tar.xz")
if not base.is_dir("./qt-everywhere-opensource-src-5.9.9"):
base.cmd("tar", ["-xf", "./qt_source_5.9.9.tar.xz"])
@ -57,12 +58,7 @@ def install_qt():
return
def install_qt_prebuild():
url_amd64 = "https://s3.eu-west-1.amazonaws.com/static-doc.teamlab.eu.com/qt/5.9.9/linux_amd64/qt_binary.7z"
base.download(url_amd64, "./qt_amd64.7z")
base.extract("./qt_amd64.7z", "./qt_build")
base.create_dir("./qt_build/Qt-5.9.9")
base.cmd("mv", ["./qt_build/qt_amd64", "./qt_build/Qt-5.9.9/gcc_64"])
base.setup_local_qmake("./qt_build/Qt-5.9.9/gcc_64/bin")
base.cmd("python3", ["qt_binary_fetch.py", "all"])
return
if not base.is_file("./node_js_setup_14.x"):
@ -71,7 +67,10 @@ if not base.is_file("./node_js_setup_14.x"):
if not base.is_dir("./qt_build"):
print("install qt...")
install_qt_prebuild()
if base.get_env("DO_NOT_USE_PREBUILD_QT") == "1":
qt_binary_build.install_qt()
else:
install_qt_prebuild()
branch = get_branch_name("../..")

5
tools/linux/cmake.sh Executable file
View File

@ -0,0 +1,5 @@
wget https://github.com/Kitware/CMake/releases/download/v3.30.0/cmake-3.30.0-linux-x86_64.tar.gz
tar -xzf cmake-3.30.0-linux-x86_64.tar.gz -C /opt
ln -s /opt/cmake-3.30.0-linux-x86_64/bin/cmake /usr/local/bin/cmake
ln -s /opt/cmake-3.30.0-linux-x86_64/bin/ctest /usr/local/bin/ctest
rm cmake-3.30.0-linux-x86_64.tar.gz

View File

@ -16,6 +16,7 @@ def install_deps():
"build-essential",
"ca-certificates",
"cmake",
"perl",
"curl",
"git",
"glib-2.0-dev",
@ -43,7 +44,8 @@ def install_deps():
"libncurses6",
"curl",
"libxkbcommon-dev",
"libxkbcommon-x11-dev"]
"libxkbcommon-x11-dev",
"libnotify-dev"]
for package in packages:
base.cmd("sudo", ["apt-get", "install", "-y", package], True)
@ -67,7 +69,7 @@ def install_deps():
print("OK")
base.cmd("sudo", ["apt-get", "-y", "install", "npm", "yarn"], True)
base.cmd("sudo", ["npm", "install", "-g", "grunt-cli"])
base.cmd("sudo", ["npm", "install", "-g", "pkg"])
base.cmd("sudo", ["npm", "install", "-g", "@yao-pkg/pkg@6.11.0"])
# java
java_error = base.cmd("sudo", ["apt-get", "-y", "install", "openjdk-11-jdk"], True)

16
tools/linux/python.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
wget https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/python/python3.tar.gz
wget https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/python/extract.sh
chmod +x ./extract.sh
./extract.sh
cd ./python3/bin
ln -s python3 python
cd ../../
rm ./extract.sh
rm ./python3.tar.gz

Binary file not shown.

47
tools/linux/qt_binary_build.py Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python
import sys
sys.path.append('../../scripts')
import base
import os
import subprocess
import deps
def install_qt():
# qt
if not base.is_file("./qt_source_5.9.9.tar.xz"):
base.download("https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/qt/qt-everywhere-opensource-src-5.9.9.tar.xz", "./qt_source_5.9.9.tar.xz")
if not base.is_dir("./qt-everywhere-opensource-src-5.9.9"):
base.cmd("tar", ["-xf", "./qt_source_5.9.9.tar.xz"])
qt_params = ["-opensource",
"-confirm-license",
"-release",
"-shared",
"-accessibility",
"-prefix",
"./../qt_build/Qt-5.9.9/gcc_64",
"-qt-zlib",
"-qt-libpng",
"-qt-libjpeg",
"-qt-xcb",
"-qt-pcre",
"-no-sql-sqlite",
"-no-qml-debug",
"-gstreamer", "1.0",
"-nomake", "examples",
"-nomake", "tests",
"-skip", "qtenginio",
"-skip", "qtlocation",
"-skip", "qtserialport",
"-skip", "qtsensors",
"-skip", "qtxmlpatterns",
"-skip", "qt3d",
"-skip", "qtwebview",
"-skip", "qtwebengine"]
base.cmd_in_dir("./qt-everywhere-opensource-src-5.9.9", "./configure", qt_params)
base.cmd_in_dir("./qt-everywhere-opensource-src-5.9.9", "make", ["-j", "4"])
base.cmd_in_dir("./qt-everywhere-opensource-src-5.9.9", "make", ["install"])
return

67
tools/linux/qt_binary_fetch.py Executable file
View File

@ -0,0 +1,67 @@
#!/usr/bin/env python3
import sys
import os
sys.path.append('../../scripts')
import base
URL = "https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/qt/"
SYSROOTS = {
"amd64": "qt_binary_5.9.9_gcc_64.7z",
"arm64": "qt_binary_5.9.9_gcc_arm64.7z",
}
COMPILERS = {
"amd64": "gcc_64",
"arm64": "gcc_arm64",
}
def download_and_extract(name):
cur_dir = os.getcwd()
os.chdir("./qt_build/Qt-5.9.9")
archive = SYSROOTS[name]
folder = "./" + COMPILERS[name]
if (base.is_dir(folder)):
base.delete_dir(folder)
archive_file = "./" + archive
base.download(URL + archive, archive_file)
base.extract(archive_file, "./")
os.chdir(cur_dir)
base.setup_local_qmake("./qt_build/Qt-5.9.9/" + COMPILERS[name] + "/bin")
def main():
if len(sys.argv) != 2:
print("Usage: fetch.py [amd64|arm64|all]")
sys.exit(1)
target = sys.argv[1]
if not base.is_dir("./qt_build/Qt-5.9.9"):
base.create_dir("./qt_build/Qt-5.9.9")
targets = []
if ("all" == target):
targets.append("amd64")
targets.append("arm64")
elif ("arm64" == target) and not base.is_os_arm():
targets.append("amd64")
targets.append(target)
else:
targets.append(target)
if (0 == len(targets)):
print(f"Unknown target: {target}")
print("Valid values: amd64, arm64, all")
sys.exit(1)
for name in targets:
download_and_extract(name)
if "arm64" in targets and not base.is_os_arm():
base.move_dir("./qt_build/Qt-5.9.9/gcc_arm64/bin", "./qt_build/Qt-5.9.9/gcc_arm64/_bin")
base.copy_dir("./qt_build/Qt-5.9.9/gcc_64/bin", "./qt_build/Qt-5.9.9/gcc_arm64/bin")
if __name__ == "__main__":
main()

2
tools/linux/sysroot/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*sysroot*

View File

@ -0,0 +1,19 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_MAKE_PROGRAM "/usr/bin/make" CACHE FILEPATH "" FORCE)
set(CMAKE_C_COMPILER $ENV{CC})
set(CMAKE_CXX_COMPILER $ENV{CXX})
set(CMAKE_AR $ENV{AR})
set(CMAKE_RANLIB $ENV{RANLIB})
set(CMAKE_C_FLAGS "$ENV{CFLAGS}" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS}" CACHE STRING "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS "$ENV{LDFLAGS}" CACHE STRING "" FORCE)
set(CMAKE_SHARED_LINKER_FLAGS "$ENV{LDFLAGS}" CACHE STRING "" FORCE)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

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