Compare commits

...

222 Commits

Author SHA1 Message Date
23db442c82 [build] Downgrade to nodejs packaging version to 20 2025-05-10 16:14:27 +03:00
4d4d1612ce [build] Bump nodejs version to 22(linux only) 2025-05-08 01:22:34 +03:00
ded3dfa63c [build] Bump nodejs version to 22 2025-05-08 01:22:33 +03:00
227ecbde99 [deploy] Add check is_exist for addon path 2025-04-22 16:08:42 +03:00
d288d6326c Merge branch hotfix/v8.3.3 into develop 2025-04-21 08:57:49 +00:00
1539c187e3 Merge pull request 'Up version to 8.3.3' (#72) from fix/version8.3.3 into hotfix/v8.3.3
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/72
2025-04-14 10:11:35 +00:00
78df8eb494 Up version to 8.3.3 2025-04-14 14:52:43 +05:00
eebbd513d3 Added Python script for creating and launching the Libreoffice build 2025-04-08 09:47:48 +00:00
74c02f9d50 Merge branch hotfix/v8.3.2 into hotfix/v8.3.3 2025-04-01 08:32:42 +00:00
25a1e16824 Merge branch hotfix/v8.3.2 into develop 2025-04-01 08:32:41 +00:00
2b07d1aa4d Fix build 2025-04-01 10:50:23 +03:00
4c76406f8c Fix folder creation 2025-03-26 15:49:50 +03:00
6fd89057ec Fix typo 2025-03-26 15:41:48 +03:00
ae8b77628e Fix build 2025-03-26 15:31:21 +03:00
959d919d9e Merge pull request 'hotfix/v8.3.2' (#68) from hotfix/v8.3.2 into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/68
2025-03-21 11:44:01 +00:00
3589ea0f60 Fix bug with duplicate projects 2025-03-21 10:24:44 +03:00
ad23ee2803 Merge branch hotfix/v8.3.2 into master 2025-03-19 12:43:41 +00:00
bc59f739f5 Merge pull request 'Up version to 8.3.2' (#66) from fix/version8.3.2 into hotfix/v8.3.2
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/66
2025-03-18 11:09:00 +00:00
b8e42184f8 Up version to 8.3.2 2025-03-18 15:54:05 +05:00
520d779f04 [develop] Add information log message to restart_win_rabbit 2025-03-17 10:55:53 +03:00
c4b938b7db [deploy] Build and deploy server grunt module 2025-03-16 18:23:22 +03:00
9435cdc99b Merge pull request 'Added html 3dParty version control' (#64) from fix/html into hotfix/v8.3.2
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/64
2025-03-12 16:54:41 +00:00
cf90a5ce21 Added html 3dParty version control 2025-03-12 19:50:03 +03:00
12ce537781 [develop] Parse config to fix install_mysqlserver 2025-03-10 20:52:36 +03:00
12d824fe2d Merge branch hotfix/v8.3.1 into develop 2025-03-10 08:25:10 +00:00
de33755900 Remove unused files 2025-03-06 15:06:49 +03:00
6e87116634 Fix typo 2025-03-06 13:56:55 +03:00
029b16ca68 Fix deploy builder 2025-03-06 13:05:25 +03:00
6a9b2bac4a Merge pull request 'feature/build-lo' (#51) from feature/build-lo into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/51
2025-03-04 08:42:49 +00:00
87542f4a56 [license_checker] Update config 2025-02-28 12:17:58 +03:00
32b47cd21e Merge branch hotfix/v8.3.1 into master 2025-02-27 13:16:17 +00:00
bf75e1c062 Merge pull request 'Up version to 8.3.1' (#50) from fix/version8.3.1 into hotfix/v8.3.1
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/50
2025-02-27 13:15:05 +00:00
97fccfa34b Up version to 8.3.1 2025-02-27 12:07:55 +05:00
1ed32fe71c [build] added disable_sln flag 2025-02-26 10:57:24 +03:00
3ce8f251a1 [build] fix build_lo_windows.py added build_vs_integration 2025-02-26 10:40:03 +03:00
e2ad38f297 [develop] Add "vs-ide-integration" make param 2025-02-25 21:48:52 +03:00
993303bfa4 [develop] Add "lo_build_path" param; Remove "requests" import; Fix LO build 2025-02-24 23:09:54 +03:00
50d9460f63 [build] fix build_lo_windows.py 2025-02-24 12:28:42 +03:00
4b02b57c07 [build] added script for building LO on Windows 2025-02-21 06:56:57 +03:00
1fc9382ce9 Merge pull request 'hotfix/v8.3.1' (#49) from hotfix/v8.3.1 into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/49
2025-02-20 13:31:20 +00:00
ea43e67fe8 Merge pull request '[jsdoc] Added editor name to examples' (#48) from fix/js-doc into hotfix/v8.3.1
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/48
2025-02-19 17:34:37 +00:00
dd28a41e17 [jsdoc] Added editor name to examples 2025-02-20 00:30:45 +07:00
b11a273d65 Merge pull request '[jsdoc] Remove model field if empty' (#46) from fix/js-doc into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/46
2025-02-12 05:45:02 +00:00
d4ee25b004 [jsdoc] Remove model field if empty 2025-02-12 12:35:28 +07:00
a2b7719100 [v8] Disable git configuration on windows 2025-02-11 21:32:05 +03:00
1e6cde4d98 Merge pull request '[jsdoc] Added generation dataset script' (#45) from feauture/dataset-script into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/45
2025-02-11 08:05:25 +00:00
34f627d146 [jsdoc] Added generation dataset script 2025-02-11 15:01:58 +07:00
54accd4394 Merge pull request '[jsdoc] Escape "|" in tables md' (#44) from fix/js-doc into hotfix/v8.3.1
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/44
2025-02-11 07:56:03 +00:00
63557fba56 [jsdoc] Escape "|" in tables md 2025-02-11 14:54:14 +07:00
7a4be158c2 Merge pull request 'Online installer: switch to MSVC2015' (#41) from feature/online-installer-msvc2015 into develop 2025-02-06 15:30:10 +00:00
810e12bd22 Merge release/8.3.0 into master 2025-02-06 07:41:46 +00:00
066f7ad8c1 Version Up 2025-02-06 10:31:28 +03:00
e52a654731 Merge branch release/v8.3.0 into master 2025-02-05 17:13:36 +00:00
370879f636 [jsdoc] Code block for expression 2025-01-31 17:18:30 +07:00
170a511654 [win] online installer: switch to MSVC2015 2025-01-30 13:24:51 +02:00
679afe1bc4 Fix generation on mac 2025-01-27 10:18:20 +03:00
8b5cfff24a [jsdoc] Remove output dirs before generation 2025-01-27 06:48:12 +00:00
27de97031e Merge pull request '[jsdoc] FIx paths in enums' (#39) from fix/js-doc into release/v8.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/39
2025-01-27 06:37:46 +00:00
8ee874da14 [jsdoc] FIx paths in enums 2025-01-27 13:23:58 +07:00
11c783f088 Merge pull request 'release/v8.3.0' (#38) from release/v8.3.0 into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/38
2025-01-26 07:48:28 +00:00
a3cb31291f [desktop] debug 2025-01-23 13:32:21 +03:00
6a43b86912 [desktop] for bug 62528 2025-01-23 11:15:01 +03:00
21bb535ee0 [jsdoc] Added plugins md docs generation 2025-01-22 12:49:26 +00:00
9ea948b825 [jsdoc] Fix generation 2025-01-22 12:49:26 +00:00
fe2fad9378 [desktop] refactoring 2025-01-22 13:00:50 +03:00
d566ffd9fa Fix mac builder packages build 2025-01-21 12:22:46 +03:00
370b23f38f Fix md syntax 2025-01-20 21:03:27 +03:00
253ee696be Merge pull request '[desktop] remove folders for IE from web-apps' (#35) from feature/desktop-clean-ie-folder into release/v8.3.0 2025-01-20 16:18:08 +00:00
e08c6f79bc [desktop] remove folders for IE from web-apps 2025-01-20 19:16:03 +03:00
4240319fef [develop] Print error in check_mysqlServer 2025-01-17 11:23:20 +03:00
e1aaa2415b Merge pull request 'Delete unnecessary files on builder deploy' (#34) from feature/minimize-builder-deploy into release/v8.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/34
2025-01-15 11:51:28 +00:00
e71eb56630 Fix builder packages build 2025-01-15 11:25:06 +03:00
38496f2971 Delete unnecessary files on builder deploy 2025-01-14 20:04:54 +04:00
d1c7d8d9f6 HWPFile added to build_tools 2025-01-13 14:06:16 +03:00
36fdfd672f Merge pull request 'Fix bug 72349' (#30) from fix/bug72349 into release/v8.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/30
2025-01-10 10:03:10 +00:00
55c0f61189 Fix bug 72349 2025-01-10 13:50:59 +04:00
053e317850 Merge pull request 'release/v8.3.0' (#29) from release/v8.3.0 into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/29
2025-01-02 19:54:05 +00:00
38296bf292 Merge pull request '[jsdoc][plugins] Fixed reading common methods' (#28) from fix/build-jsdoc-plugins into release/v8.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/28
2024-12-22 13:34:46 +00:00
f0ba4564cc [jsdoc][plugins] Fixed reading common methods 2024-12-22 19:53:43 +07:00
21ec70214d fix Bug 70053 - Fix warning with JSONArgsRecommended 2024-12-20 10:47:39 +00:00
6d1a8376ba Merge pull request '[jsdoc] Fixes for build plugins api' (#23) from fix/build-jsdoc-plugins into release/v8.3.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/23
2024-12-18 07:42:02 +00:00
0ca83fe152 [jsdoc] Fixes for build plugins api 2024-12-16 18:44:35 +07:00
2301c407a2 Remove HtmlRenderer library 2024-12-10 14:33:10 +03:00
d6096431bd Merge branch hotfix/v8.2.2 into develop 2024-12-09 11:57:11 +00:00
d7532d5b83 Merge branch hotfix/v8.2.2 into release/v8.3.0 2024-12-09 11:57:11 +00:00
c7d805f8df Up iwork version 2024-12-02 17:02:51 +03:00
d78ab30cdf Merge pull request 'Online-installer: add dev-channel support' (#21) from feature/online-installer-dev-channel into develop 2024-11-28 14:06:02 +00:00
c123f77195 [win] online-installer: add dev-channel support 2024-11-28 15:30:47 +02:00
a60bc78e23 Merge branch hotfix/v8.2.2 into master 2024-11-28 12:13:38 +00:00
78ee107e85 Fix build builder with branding onlyoffice 2024-11-28 14:57:00 +03:00
12c3310451 Up version 2024-11-27 20:01:36 +03:00
d525d8f603 Add change author for templates 2024-11-24 22:50:21 +03:00
337d1095dc Up iwork version 2024-11-22 17:51:36 +03:00
fab40cb6b3 Reformat code 2024-11-22 17:13:08 +03:00
f4cdc1aecd Merge pull request 'Update iwork build' (#20) from fix/iwork into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/20
2024-11-22 13:45:29 +00:00
f702e3245a Update iwork build 2024-11-22 15:38:56 +03:00
d890ba4f43 Fix ds prerequisites build 2024-11-22 12:48:32 +03:00
d929ed411f Fix typo 2024-11-22 11:34:58 +03:00
55daa28d74 Merge pull request 'hotfix/v8.2.2' (#18) from hotfix/v8.2.2 into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/18
2024-11-22 08:26:38 +00:00
2bab12aad1 Merge pull request 'feature/iwork' (#17) from feature/iwork into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/17
2024-11-22 07:37:28 +00:00
80fb376132 Fix bug 71595 2024-11-21 17:29:22 +03:00
1d557f1065 Fix typo 2024-11-21 15:10:51 +03:00
30df3df8cf Setup qmake with portable data 2024-11-21 14:32:00 +03:00
02b4655a16 Refactoring 2024-11-20 16:07:21 +03:00
debf0158d4 iWork added to build_tools 2024-11-19 14:05:51 +03:00
0f730c1948 Write macos update changelog placeholder 2024-11-19 08:03:36 +00:00
fa7e324fe0 Add deps 2024-11-18 18:30:05 +03:00
e2313e6a3d fix Bug 71385 - Fix issue with DocsApi 2024-11-18 10:04:31 +00:00
2ce8c42323 Remove errors with setup system git 2024-11-18 11:57:30 +03:00
684e65adaa Skip errors from git config 2024-11-17 14:08:07 +03:00
a8fc3fb2f1 Add DocumentServer Prerequisites installer build 2024-11-15 18:10:04 +03:00
68bcdb2f88 Merge branch hotfix/v8.2.1 into master 2024-11-12 12:48:18 +00:00
af3627bccb Fix build 2024-11-11 16:50:32 +03:00
4cbe032363 Add online installer upload 2024-11-07 15:47:57 +03:00
5e4b3cf0d2 Fix build with modern compilers 2024-11-05 14:22:50 +03:00
593af1048b Remove windows changelog deploy 2024-11-02 12:44:00 +00:00
ae00ecb773 Fix web-apps closure maps deploy 2024-11-02 12:44:00 +00:00
da83e42172 Refactor packages deploy 2024-11-02 12:44:00 +00:00
2895d53f8e Fix mac old zip copy 2024-11-01 15:45:38 +03:00
10d1f22ec3 Merge pull request 'Deploy online-installer' (#10) from feature/deploy-online-installer into develop
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/10
2024-10-29 18:20:12 +00:00
4ed1e64a61 [win] online-installer: temporarily comment out upload stage 2024-10-29 18:44:18 +02:00
6402936285 [win] deploy online-installer 2024-10-29 14:59:03 +02:00
e01e5c145a Update scripts/sdkjs_common/jsdoc/get_latest_branch.py 2024-10-29 06:52:02 +00:00
56f6d82c8f Update scripts/sdkjs_common/jsdoc/get_latest_branch.py 2024-10-29 06:50:29 +00:00
3e79cf0c12 Add get latest branch 2024-10-29 05:57:06 +00:00
efc09657a8 Add get latest branch 2024-10-29 05:54:44 +00:00
64390c3e01 Fix build v8 on mac 2024-10-24 14:00:14 +03:00
513edb802d Merge branch release/v8.2.0 into develop 2024-10-21 11:46:47 +00:00
52c35b8e3c Merge branch release/v8.2.0 into master 2024-10-17 11:08:22 +00:00
cf1c25031c Update version 2024-10-17 12:45:40 +03:00
7b9f18867a Revert all remplates to desktop for macos 2024-10-10 10:51:28 +03:00
0985b4dbe8 Add script for deploy templates 2024-10-10 02:45:16 +03:00
772fb721ae Add data for hard update hunspell module 2024-10-09 10:48:59 +03:00
1ef1c795c1 Merge pull request '[desktop] debug macOS app launch' (#4) from fix/mac-launch-with-templates into release/v8.2.0 2024-10-08 22:15:31 +00:00
6d956566c5 [desktop] debug 2024-10-09 01:13:47 +03:00
edec5bb25f [desktop] debug macOS app launch 2024-10-09 01:08:19 +03:00
3534f65f0e Remove empty items in plugins list 2024-10-04 18:10:31 +03:00
6fbea9c8a4 Fix build v8 on macos with xcode 16+ 2024-10-03 21:54:17 +03:00
18bba5da3d Disable bitcode by default 2024-10-03 13:49:58 +03:00
952270e1ba Fix rpaths 2024-10-02 13:51:17 +00:00
0c180e6ee5 Fix deploy automation API 2024-09-27 12:13:45 +00:00
fdd9c329b1 Fix deploy automation API 2024-09-26 16:51:44 +00:00
5b80459b37 Merge pull request 'feature/templates-for-desktop' (#1) from feature/templates-for-desktop into release/v8.2.0
Reviewed-on: https://git.onlyoffice.com/ONLYOFFICE/build_tools/pulls/1
2024-09-18 09:52:59 +00:00
1b646a6e00 [desktop] correct path 2024-09-18 12:51:45 +03:00
cf970efbec [desktop] copy templates to package 2024-09-18 12:32:18 +03:00
4020cdac69 Merge branch hotfix/v8.1.3 into release/v8.2.0 2024-09-13 10:35:16 +00:00
2415c2ffe8 Merge branch hotfix/v8.1.3 into develop 2024-09-13 09:46:47 +00:00
d41502ea19 Merge branch hotfix/v8.1.3 into master 2024-09-13 09:46:46 +00:00
f5d0ef4005 Fix macos bug 2024-09-11 11:21:30 +03:00
c4a89ecf61 Fix update 2024-09-10 14:23:17 +03:00
71eb25e561 Remove unused files 2024-09-10 09:58:13 +03:00
486a6683fd Add dependencies for doctrenderer 2024-09-09 06:38:13 +03:00
2175d8d87c Update version 2024-09-05 10:38:13 +03:00
f463bff49e Fixes for working by ssh 2024-09-03 15:09:37 +03:00
a817e2b046 Update generate_docs_plugins_json.py
delete generating missing files with json
2024-09-01 00:34:25 +03:00
3539e36bde Update generate_docs_json.py
delete generate missing examples with json files
2024-09-01 00:32:50 +03:00
6930a9ffe1 Update generate_docs_md.py 2024-08-31 12:58:41 +03:00
e0a44502b1 Update generate_docs_md.py 2024-08-31 12:49:30 +03:00
19e1bd5586 Update generate_docs_md.py 2024-08-31 10:27:23 +03:00
ea65ba02f1 Update generate_docs_md.py 2024-08-31 10:23:46 +03:00
8406e48009 Update generate_docs_md.py 2024-08-31 10:14:28 +03:00
a8f1d11cbc Update generate_docs_md.py 2024-08-31 01:48:55 +03:00
f245a4a9c6 Update generate_docs_md.py 2024-08-31 01:40:00 +03:00
597529a16d Fix crossbuild 2024-08-30 10:49:09 +03:00
9b9dba05c2 Update generate_docs_plugins_json.py
delete branch from path
2024-08-30 09:36:10 +03:00
2d0bbc824f Update generate_docs_json.py
delete branch name from path
2024-08-30 09:33:34 +03:00
fa523c673f Update generate_docs_plugins_json.py
change default
2024-08-30 09:29:54 +03:00
da1a4ba393 Update README.md
change default
2024-08-30 09:29:44 +03:00
e9c9712e52 Update generate_docs_json.py
change default path
2024-08-30 09:25:34 +03:00
78561ca659 Update README.md 2024-08-30 09:25:23 +03:00
1ad87383e3 Update README.md
fix Requirements and Installation
2024-08-30 08:31:45 +03:00
c29ac1549f Update README.md
fix default path
2024-08-30 08:26:58 +03:00
f09eeb19e5 Fix generation on linux 2024-08-29 23:19:21 +03:00
4b7b2c78a2 Merge pull request #871 from ONLYOFFICE/fix/jsdoc
[jsdoc] Fixed style
2024-08-29 00:31:34 -07:00
414af6bdb0 [jsdoc] Fixed style 2024-08-29 14:27:47 +07:00
df7288b275 Merge pull request #868 from ONLYOFFICE/feature/extend-apijs-load
[web-apps] copy api.js as api.js.tpl for server package
2024-08-28 14:39:25 +03:00
ce80953086 Merge pull request #870 from ONLYOFFICE/fix/jsdoc
[jsdoc] Fixed creating data types.
2024-08-28 04:05:29 -07:00
d1344dab71 [jsdoc] Fixed creating data types. 2024-08-28 18:00:53 +07:00
4f2ba4ae76 Merge pull request #869 from ONLYOFFICE/fix/jsdocs
[jsdoc] Fixed output path
2024-08-28 03:32:34 -07:00
6bd525c3b4 [jsdoc] Fixed output path 2024-08-28 17:29:12 +07:00
341671a612 Fix typo 2024-08-28 13:10:29 +03:00
9161aa1556 Add generate snapshots 2024-08-28 13:06:12 +03:00
70e9fbabce [web-apps] copy api.js as api.js.tpl for server package 2024-08-27 21:45:15 +03:00
a2c00deba2 Add function for portable test utilities 2024-08-26 14:20:14 +03:00
9b4ef9d1d7 [web-apps] rename check translation script 2024-08-23 12:52:28 +03:00
3baee0c14e Move pro to builder module 2024-08-22 14:07:05 +03:00
0508bf43d1 Merge pull request #867 from ONLYOFFICE/feature/webapps-fix-translations
[web-apps] check translation for 'main' apps before build
2024-08-20 22:15:14 +03:00
bd279d1ad7 Merge pull request #861 from ONLYOFFICE/feature/docbuilder-java
Build and deploy docbuilder Java wrapper
2024-08-19 04:51:08 -07:00
4d55a66307 Merge pull request #865 from ONLYOFFICE/fix/jsdoc
[jsdoc] Replacing line breaks with spaces
2024-08-16 06:09:23 -07:00
9481e01581 [jsdoc] Replacing line breaks with spaces 2024-08-16 20:04:51 +07:00
fe91bf9620 Merge pull request #864 from ONLYOFFICE/fix/jsdocs
[jsdoc] Fixed paths generation
2024-08-16 05:30:20 -07:00
d812ba379b [jsdoc] Fixed paths generation 2024-08-16 19:28:56 +07:00
e1cc7f3c83 Fix libraries not loading on mac 2024-08-14 19:24:34 +04:00
f50d5d2cd1 Fix path problems on mac 2024-08-14 15:54:07 +04:00
b3987b0ad5 Move build of Java wrapper to build_sln.py 2024-08-13 20:42:54 +04:00
243946a189 Merge pull request #863 from ONLYOFFICE/release/v8.2.0
Merge branch release/v8.2.0 into develop
2024-08-13 07:46:12 -07:00
63fbbc5603 Add missed library to deploy 2024-08-13 17:26:14 +03:00
fcb857df69 [web-apps] check translation for 'main' apps before build 2024-08-13 11:19:18 +03:00
dabbc31c09 Handling complex dependencies in project file 2024-08-13 10:38:33 +03:00
997bfa3dd5 Fix typo 2024-08-13 09:14:23 +03:00
50eca8aab5 Fix build 2024-08-13 07:45:29 +03:00
6e4a2e4d5e Add dictionariestester to core deploy 2024-08-13 00:25:56 +03:00
40e9938885 Add test for dicts & spellmodule to core 2024-08-13 00:25:06 +03:00
5bc8ca2266 Build and deploy JAR 2024-08-12 18:42:18 +04:00
4cdbfbfb86 Deploy JNI helper dynamic library 2024-08-12 18:24:51 +04:00
01575d1f2e Fix core and builder archive deploy (#860)
* Refactoring script parameters

* Add builder 7z deploy

* Refactoring core 7z deploy

* Small fix
2024-08-12 16:53:56 +03:00
8f75c75b80 Merge pull request #859 from ONLYOFFICE/fix/builder-docs
[jsdoc][bu] Removed example filed from json docs
2024-08-07 04:24:42 -07:00
ebc084f9ea [jsdoc][bu] Removed example filed from json docs 2024-08-07 18:24:02 +07:00
626efaf5cf Merge pull request #858 from ONLYOFFICE/fix/jsdoc
[jsdoc][plugins] Added examples field to json.
2024-08-07 03:50:29 -07:00
096ce99588 [jsdoc][plugins] Added examples field to json. 2024-08-07 17:45:40 +07:00
9ce103b31b Add returncode in runcommand function 2024-08-06 17:54:39 +03:00
13cbd84b58 Change documentType for pdf 2024-08-06 14:24:12 +03:00
a8912dff41 Refactoring 2024-08-06 13:46:42 +03:00
8b773614ba Fix builder rpm package deploy (#857) 2024-08-06 11:45:54 +03:00
d04f04f382 Merge pull request #856 from ONLYOFFICE/fix/plugins-docs
Jsdocs api plugins generation script
2024-08-05 03:35:02 -07:00
9a44dae4f9 Jsdocs api plugins generation script 2024-08-05 17:28:06 +07:00
07665dd93e Merge pull request #855 from ONLYOFFICE/fix/jsdoc
Fixed jsdoc md generation
2024-08-02 07:35:32 -07:00
eeca17e78b Fixed jsdoc md generation 2024-08-02 21:30:29 +07:00
f91264bc94 Merge pull request #854 from ONLYOFFICE/fix/docs-generation
[jsdoc] Fixed api docs generation
2024-08-02 06:34:17 -07:00
10b7f63f9f Merge branch hotfix/v8.1.1 into develop 2024-07-26 08:02:44 +00:00
f2dff2d173 Merge branch hotfix/v8.1.1 into master 2024-07-26 08:02:40 +00:00
aa49605ac4 Merge branch hotfix/v8.1.1 into master 2024-07-15 11:38:25 +00:00
66 changed files with 2322 additions and 684 deletions

View File

@ -11,5 +11,4 @@ RUN rm /usr/bin/python && ln -s /usr/bin/python2 /usr/bin/python
ADD . /build_tools
WORKDIR /build_tools
CMD cd tools/linux && \
python3 ./automate.py
CMD ["sh", "-c", "cd tools/linux && python3 ./automate.py"]

View File

@ -27,7 +27,7 @@ parser.add_option("--db-pass", action="store", type="string", dest="db-pass", de
parser.add_option("--compiler", action="store", type="string", dest="compiler", default="", help="defines compiler name. It is not recommended to use it as it's defined automatically (msvc2015, msvc2015_64, gcc, gcc_64, clang, clang_64, etc)")
parser.add_option("--no-apps", action="store", type="string", dest="no-apps", default="0", help="disables building desktop apps that use qt")
parser.add_option("--themesparams", action="store", type="string", dest="themesparams", default="", help="provides settings for generating presentation themes thumbnails")
parser.add_option("--git-protocol", action="store", type="string", dest="git-protocol", default="https", help="can be used only if update is set to true - 'https', 'ssh'")
parser.add_option("--git-protocol", action="store", type="string", dest="git-protocol", default="auto", help="can be used only if update is set to true - 'https', 'ssh'")
parser.add_option("--branding", action="store", type="string", dest="branding", default="", help="provides branding path")
parser.add_option("--branding-name", action="store", type="string", dest="branding-name", default="", help="provides branding name")
parser.add_option("--branding-url", action="store", type="string", dest="branding-url", default="", help="provides branding url")

View File

@ -92,6 +92,8 @@ 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

@ -10,15 +10,17 @@ import package_utils as utils
# parse
parser = argparse.ArgumentParser(description="Build packages.")
parser.add_argument("-P", "--platform", dest="platform", type=str,
action="store", help="Defines platform", required=True)
parser.add_argument("-T", "--targets", dest="targets", type=str, nargs="+",
action="store", help="Defines targets", required=True)
parser.add_argument("-R", "--branding", dest="branding", type=str,
action="store", help="Provides branding path")
action="store", help="Defines platform", required=True)
parser.add_argument("-T", "--targets", dest="targets", type=str, nargs="+",
action="store", help="Defines targets", required=True)
parser.add_argument("-V", "--version", dest="version", type=str,
action="store", help="Defines version")
action="store", help="Defines version")
parser.add_argument("-B", "--build", dest="build", type=str,
action="store", help="Defines build")
action="store", help="Defines build")
parser.add_argument("-H", "--branch", dest="branch", type=str,
action="store", help="Defines branch")
parser.add_argument("-R", "--branding", dest="branding", type=str,
action="store", help="Provides branding path")
args = parser.parse_args()
# vars
@ -29,13 +31,20 @@ common.targets = args.targets
common.clean = "clean" in args.targets
common.sign = "sign" in args.targets
common.deploy = "deploy" in args.targets
common.version = args.version if args.version else utils.get_env("BUILD_VERSION", "0.0.0")
common.build = args.build if args.build else utils.get_env("BUILD_NUMBER", "0")
if args.version: common.version = args.version
else: common.version = utils.get_env("PRODUCT_VERSION", "0.0.0")
utils.set_env("PRODUCT_VERSION", common.version)
utils.set_env("BUILD_VERSION", common.version)
if args.build: common.build = args.build
else: common.build = utils.get_env("BUILD_NUMBER", "0")
utils.set_env("BUILD_NUMBER", common.build)
if args.branch: common.branch = args.branch
else: common.branch = utils.get_env("BRANCH_NAME", "null")
utils.set_env("BRANCH_NAME", common.branch)
common.branding = args.branding
common.timestamp = utils.get_timestamp()
common.workspace_dir = utils.get_abspath(utils.get_script_dir(__file__) + "/..")
common.branding_dir = utils.get_abspath(common.workspace_dir + "/" + args.branding) if args.branding else common.workspace_dir
common.deploy_data = utils.get_path(common.workspace_dir + "/deploy.txt")
common.summary = []
utils.log("os_family: " + common.os_family)
utils.log("platform: " + str(common.platform))
@ -64,15 +73,14 @@ import package_mobile
# build
utils.set_cwd(common.workspace_dir, verbose=True)
utils.delete_file(common.deploy_data)
if "core" in common.targets:
package_core.make()
if "closuremaps_opensource" in common.targets:
if "closuremaps_sdkjs_opensource" in common.targets:
package_core.deploy_closuremaps_sdkjs("opensource")
package_core.deploy_closuremaps_webapps("opensource")
if "closuremaps_commercial" in common.targets:
if "closuremaps_sdkjs_commercial" in common.targets:
package_core.deploy_closuremaps_sdkjs("commercial")
package_core.deploy_closuremaps_webapps("commercial")
if "closuremaps_webapps" in common.targets:
package_core.deploy_closuremaps_webapps("opensource")
if "desktop" in common.targets:
package_desktop.make()
if "builder" in common.targets:
@ -83,6 +91,8 @@ if "server_enterprise" in common.targets:
package_server.make("enterprise")
if "server_developer" in common.targets:
package_server.make("developer")
if "server_prerequisites" in common.targets:
package_server.make("prerequisites")
if "mobile" in common.targets:
package_mobile.make()

View File

@ -39,6 +39,9 @@ def is_os_arm():
return False
return True
def get_platform():
return platform.machine().lower()
def is_python_64bit():
return (struct.calcsize("P") == 8)
@ -172,6 +175,13 @@ def find_file(path, pattern):
for filename in fnmatch.filter(filenames, pattern):
return os.path.join(root, filename)
def find_files(path, pattern):
result = []
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, pattern):
result.append(os.path.join(root, filename))
return result
def create_dir(path):
path2 = get_path(path)
if not os.path.exists(path2):
@ -367,7 +377,7 @@ def cmd(prog, args=[], is_no_errors=False):
else:
command = prog
for arg in args:
command += (" \"" + arg + "\"")
command += (" \"" + arg.replace('\"', '\\\"') + "\"")
ret = subprocess.call(command, stderr=subprocess.STDOUT, shell=True)
if ret != 0 and True != is_no_errors:
sys.exit("Error (" + prog + "): " + str(ret))
@ -403,7 +413,7 @@ def cmd_exe(prog, args, is_no_errors=False):
else:
command = prog
for arg in args:
command += (" \"" + arg + "\"")
command += (" \"" + arg.replace('\"', '\\\"') + "\"")
process = subprocess.Popen(command, stderr=subprocess.STDOUT, shell=True, env=env_dir)
ret = process.wait()
if ret != 0 and True != is_no_errors:
@ -418,6 +428,13 @@ def cmd_in_dir(directory, prog, args=[], is_no_errors=False):
os.chdir(cur_dir)
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
def cmd_and_return_cwd(prog, args=[], is_no_errors=False):
cur_dir = os.getcwd()
ret = cmd(prog, args, is_no_errors)
@ -426,12 +443,13 @@ def cmd_and_return_cwd(prog, args=[], is_no_errors=False):
def run_command(sCommand):
popen = subprocess.Popen(sCommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
result = {'stdout' : '', 'stderr' : ''}
result = {'stdout' : '', 'stderr' : '', 'returncode' : 0}
try:
stdout, stderr = popen.communicate()
popen.wait()
result['stdout'] = stdout.strip().decode('utf-8', errors='ignore')
result['stderr'] = stderr.strip().decode('utf-8', errors='ignore')
result['returncode'] = popen.returncode
finally:
popen.stdout.close()
popen.stderr.close()
@ -496,12 +514,37 @@ def set_cwd(dir):
return
# git ---------------------------------------------------
def git_get_origin():
cur_dir = os.getcwd()
os.chdir(get_script_dir() + "/../")
ret = run_command("git config --get remote.origin.url")["stdout"]
os.chdir(cur_dir)
return ret
def git_is_ssh():
git_protocol = config.option("git-protocol")
if (git_protocol == "https"):
return False
if (git_protocol == "ssh"):
return True
origin = git_get_origin()
if (git_protocol == "auto") and (origin.find(":ONLYOFFICE/") != -1):
return True
return False
def get_ssh_base_url():
cur_origin = git_get_origin()
ind = cur_origin.find(":ONLYOFFICE/")
if (ind == -1):
return "git@github.com:ONLYOFFICE/"
return cur_origin[:ind+12]
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"
if config.option("git-protocol") == "ssh":
url = "git@github.com:ONLYOFFICE/" + repo + ".git"
if git_is_ssh():
url = get_ssh_base_url() + repo + ".git"
folder = get_script_dir() + "/../../" + repo
if is_current_dir:
folder = repo
@ -534,10 +577,12 @@ def get_repositories():
result.update(get_sdkjs_addons())
result["onlyoffice.github.io"] = [False, False]
result["web-apps"] = [False, False]
result.update(get_web_apps_addons())
result["dictionaries"] = [False, False]
result["core-fonts"] = [False, False]
if config.check_option("module", "server"):
result.update(get_web_apps_addons())
if config.check_option("module", "builder"):
result["document-templates"] = [False, False]
@ -570,8 +615,8 @@ 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"
if config.option("git-protocol") == "ssh":
url = "git@github.com:ONLYOFFICE/" + repo + ".git"
if git_is_ssh():
url = get_ssh_base_url() + repo + ".git"
folder = get_script_dir() + "/../../" + repo
if is_current_dir:
folder = repo
@ -738,8 +783,9 @@ def qt_config(platform):
if (-1 != platform.find("xp")):
config_param += " build_xp"
if ("ios" == platform):
set_env("BITCODE_GENERATION_MODE", "bitcode")
set_env("ENABLE_BITCODE", "YES")
if (config.check_option("bitcode", "yes")):
set_env("BITCODE_GENERATION_MODE", "bitcode")
set_env("ENABLE_BITCODE", "YES")
config_param = config_param.replace("desktop", "")
config_param += " iphoneos device"
if (-1 == config_param_lower.find("debug")):
@ -1187,18 +1233,19 @@ def mac_correct_rpath_x2t(dir):
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"])
mac_correct_rpath_library("doctrenderer", ["UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "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("HtmlRenderer", ["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("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", "HtmlRenderer", "XpsFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "doctrenderer", "DocxRenderer"])
mac_correct_rpath_binary("./x2t", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "doctrenderer", "DocxRenderer", "IWorkFile", "HWPFile"])
if is_file("./allfontsgen"):
cmd("chmod", ["-v", "+x", "./allfontsgen"])
cmd("install_name_tool", ["-add_rpath", "@executable_path", "./allfontsgen"], True)
@ -1206,7 +1253,7 @@ def mac_correct_rpath_x2t(dir):
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"])
mac_correct_rpath_binary("./allthemesgen", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "graphics", "kernel_network", "doctrenderer", "PdfFile", "XpsFile", "DjVuFile", "DocxRenderer"])
if is_file("./pluginsmanager"):
cmd("chmod", ["-v", "+x", "./pluginsmanager"])
cmd("install_name_tool", ["-add_rpath", "@executable_path", "./pluginsmanager"], True)
@ -1223,9 +1270,14 @@ def mac_correct_rpath_docbuilder(dir):
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", "HtmlRenderer", "XpsFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "doctrenderer", "DocxRenderer"])
mac_correct_rpath_library("docbuilder.c", ["icudata.58", "icuuc.58", "UnicodeConverter", "kernel", "kernel_network", "graphics", "doctrenderer"])
cmd("install_name_tool", ["-add_rpath", "@loader_path", "libdocbuilder.c.dylib"], 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"])
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"])
os.chdir(cur_dir)
return
@ -1235,9 +1287,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", "HtmlRenderer", "XpsFile", "DjVuFile", "hunspell", "ooxmlsignature"])
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", "HtmlRenderer", "XpsFile", "DjVuFile", "hunspell", "ooxmlsignature"])
mac_correct_rpath_binary("./editors_helper.app/Contents/MacOS/editors_helper", ["ascdocumentscore", "UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "DjVuFile", "hunspell", "ooxmlsignature"])
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"])
@ -1271,7 +1323,7 @@ def linux_set_origin_rpath_libraries(dir, libs):
return
def linux_correct_rpath_docbuilder(dir):
linux_set_origin_rpath_libraries(dir, ["docbuilder.c.so", "icuuc.so.58", "doctrenderer.so", "graphics.so", "kernel.so", "kernel_network.so", "UnicodeConverter.so"])
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"])
return
def common_check_version(name, good_version, clean_func):
@ -1327,7 +1379,7 @@ def copy_marketplace_plugin(dst_dir, is_name_as_guid=False, is_desktop_local=Fal
git_dir = __file__script__path__ + "/../.."
if False:
# old version
base.copy_sdkjs_plugin(git_dir + "/desktop-sdk/ChromiumBasedEditors/plugins", dst_dir, "manager", is_name_as_guid, is_desktop_local)
copy_sdkjs_plugin(git_dir + "/desktop-sdk/ChromiumBasedEditors/plugins", dst_dir, "manager", is_name_as_guid, is_desktop_local)
return
src_dir_path = git_dir + "/onlyoffice.github.io/store/plugin"
name = "marketplace"
@ -1393,6 +1445,7 @@ def support_old_versions_plugins(out_dir):
def generate_sdkjs_plugin_list(dst):
plugins_list = config.option("sdkjs-plugin").rsplit(", ") \
+ config.option("sdkjs-plugin-server").rsplit(", ")
plugins_list = list(filter(None, plugins_list))
with open(get_path(dst), 'w') as file:
dump = json.dumps(sorted(plugins_list), indent=4)
file.write(re.sub(r"^(\s{4})", '\t', dump, 0, re.MULTILINE))
@ -1754,3 +1807,50 @@ def apply_patch(file, patch):
#file_content_new = "\n#if 0" + file_content_old + "#else" + file_content_new + "#endif\n"
replaceInFile(file, file_content_old, file_content_new)
return
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
if ("windows" == host_platform()):
download_platform = "win-"
elif ("linux" == host_platform()):
download_platform = "linux-"
else:
download_platform = "mac-"
download_platform += ("arm" if isArm else "")
download_platform += ("64" if is64 else "32")
else:
download_platform = download_platform.replace("_", "-")
download_build = build
if ("" == download_build):
download_build = "latest"
download_branch = branch
if ("" == download_branch):
download_branch = "develop"
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):
return
if ((platform == "linux_arm64") and not is_os_arm()):
cmd_in_dir_qemu(platform, dir, "./x2t", ["-create-js-snapshots"], True)
return
cmd_in_dir(dir, "./x2t", ["-create-js-snapshots"], True)
return
def setup_local_qmake(dir_qmake):
dir_base = os.path.dirname(dir_qmake)
writeFile(dir_base + "/onlyoffice_qt.conf", "Prefix = " + dir_base)
return

View File

@ -41,6 +41,7 @@ def make():
base.create_dir(out_dir)
# 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")
@ -55,12 +56,14 @@ def make():
base.copy_dir(base_dir + "/../sdkjs/deploy/sdkjs", out_dir + "/desktop/sdkjs")
correct_sdkjs_licence(out_dir + "/desktop/sdkjs")
base.copy_dir(base_dir + "/../web-apps/deploy/web-apps", out_dir + "/desktop/web-apps")
base.delete_dir(out_dir + "/desktop/web-apps/apps/documenteditor/embed")
base.delete_dir(out_dir + "/desktop/web-apps/apps/documenteditor/mobile")
base.delete_dir(out_dir + "/desktop/web-apps/apps/presentationeditor/embed")
base.delete_dir(out_dir + "/desktop/web-apps/apps/presentationeditor/mobile")
base.delete_dir(out_dir + "/desktop/web-apps/apps/spreadsheeteditor/embed")
base.delete_dir(out_dir + "/desktop/web-apps/apps/spreadsheeteditor/mobile")
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")
@ -168,6 +171,7 @@ def build_js_develop(root_dir):
_run_npm(root_dir + external_folder + "/web-apps/build")
_run_npm_ci(root_dir + external_folder + "/web-apps/build/sprites")
_run_grunt(root_dir + external_folder + "/web-apps/build/sprites", [])
base.cmd_in_dir(root_dir + external_folder + "/web-apps/translation", "python", ["merge_and_check.py"])
old_cur = os.getcwd()
old_product_version = base.get_env("PRODUCT_VERSION")

View File

@ -41,12 +41,13 @@ def make():
pkg_target = "node16"
if ("linux" == base.host_platform()):
pkg_target += "-linux"
#node22 packaging has issue https://github.com/yao-pkg/pkg/issues/87
pkg_target = "node20-linux"
if (-1 != config.option("platform").find("linux_arm64")):
pkg_target += "-arm64"
if ("windows" == base.host_platform()):
pkg_target += "-win"
pkg_target = "node16-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 + "/FileConverter", "pkg", [".", "-t", pkg_target, "-o", "converter"])
@ -64,10 +65,9 @@ def build_server_with_addons():
for addon in addons:
if (addon):
addon_dir = base.get_script_dir() + "/../../" + addon
base.cmd_in_dir(addon_dir, "npm", ["ci"])
base.cmd_in_dir(addon_dir, "npm", ["run", "build"])
if (base.is_exist(addon_dir)):
base.cmd_in_dir(addon_dir, "npm", ["ci"])
base.cmd_in_dir(addon_dir, "npm", ["run", "build"])
def build_server_develop():
server_dir = base.get_script_dir() + "/../../server"
base.cmd_in_dir(server_dir, "npm", ["ci"])
base.cmd_in_dir(server_dir, "grunt", ["develop", "-v"] + base.server_addons_param())
build_server_with_addons()

View File

@ -35,15 +35,41 @@ def make(solution=""):
qmake.make(platform, pro, "xcframework_platform_ios_simulator")
if config.check_option("module", "builder") and base.is_windows() and "onlyoffice" == config.branding():
# check branding libs
if (config.option("branding-name") == "onlyoffice"):
for platform in platforms:
if not platform in config.platforms:
continue
core_lib_unbranding_dir = os.getcwd() + "/../core/build/lib/" + platform + base.qt_dst_postfix()
if not base.is_dir(core_lib_unbranding_dir):
base.create_dir(core_lib_unbranding_dir)
core_lib_branding_dir = os.getcwd() + "/../core/build/onlyoffice/lib/" + platform + base.qt_dst_postfix()
base.copy_file(core_lib_branding_dir + "/doctrenderer.dll", core_lib_unbranding_dir + "/doctrenderer.dll")
base.copy_file(core_lib_branding_dir + "/doctrenderer.lib", core_lib_unbranding_dir + "/doctrenderer.lib")
# check replace
new_replace_path = base.correctPathForBuilder(os.getcwd() + "/../core/DesktopEditor/doctrenderer/docbuilder.com/src/docbuilder.h")
if ("2019" == config.option("vs-version")):
base.make_sln_project("../core/DesktopEditor/doctrenderer/docbuilder.com/src", "docbuilder.com_2019.sln")
if (True):
new_path_net = base.correctPathForBuilder(os.getcwd() + "/../core/DesktopEditor/doctrenderer/docbuilder.net/src/docbuilder.net.cpp")
base.make_sln_project("../core/DesktopEditor/doctrenderer/docbuilder.net/src", "docbuilder.net.sln")
base.restorePathForBuilder(new_path_net)
else:
base.make_sln_project("../core/DesktopEditor/doctrenderer/docbuilder.com/src", "docbuilder.com.sln")
base.restorePathForBuilder(new_replace_path)
directory_builder_branding = os.getcwd() + "/../core/DesktopEditor/doctrenderer"
if base.is_dir(directory_builder_branding):
new_replace_path = base.correctPathForBuilder(directory_builder_branding + "/docbuilder.com/src/docbuilder.h")
if ("2019" == config.option("vs-version")):
base.make_sln_project("../core/DesktopEditor/doctrenderer/docbuilder.com/src", "docbuilder.com_2019.sln")
if (True):
new_path_net = base.correctPathForBuilder(directory_builder_branding + "/docbuilder.net/src/docbuilder.net.cpp")
base.make_sln_project("../core/DesktopEditor/doctrenderer/docbuilder.net/src", "docbuilder.net.sln")
base.restorePathForBuilder(new_path_net)
else:
base.make_sln_project("../core/DesktopEditor/doctrenderer/docbuilder.com/src", "docbuilder.com.sln")
base.restorePathForBuilder(new_replace_path)
# build Java docbuilder wrapper
if config.check_option("module", "builder") and "onlyoffice" == config.branding():
for platform in platforms:
if not platform in config.platforms:
continue
# build JNI library
qmake.make(platform, base.get_script_dir() + "/../../core/DesktopEditor/doctrenderer/docbuilder.java/src/jni/docbuilder_jni.pro", "", True)
# build Java code to JAR
base.cmd_in_dir(base.get_script_dir() + "/../../core/DesktopEditor/doctrenderer/docbuilder.java", "python", ["make.py"])
return

View File

@ -16,6 +16,7 @@ import curl
import websocket_all
import v8
import html2
import iwork
import hunspell
import glew
import harfbuzz
@ -42,6 +43,7 @@ def make():
openssl.make()
v8.make()
html2.make()
iwork.make(False)
hunspell.make(False)
harfbuzz.make()
glew.make()

View File

@ -7,7 +7,27 @@ import base
import os
import subprocess
def clear_module():
directories = ["gumbo-parser", "katana-parser"]
for dir in directories:
if base.is_dir(dir):
base.delete_dir_with_access_error(dir)
def make():
old_cur_dir = os.getcwd()
print("[fetch]: html")
base_dir = base.get_script_dir() + "/../../core/Common/3dParty/html"
os.chdir(base_dir)
base.check_module_version("2", clear_module)
os.chdir(old_cur_dir)
base.cmd_in_dir(base_dir, "python", ["fetch.py"])
return
if __name__ == '__main__':
# manual compile
make()

View File

@ -3,6 +3,11 @@ sys.path.append('../../../scripts')
import base
import os
def clean():
if base.is_dir("hunspell"):
base.delete_dir_with_access_error("hunspell")
return
def make(build_js = True):
old_cur_dir = os.getcwd()
@ -11,6 +16,8 @@ def make(build_js = True):
core_common_dir = base.get_script_dir() + "/../../core/Common"
os.chdir(core_common_dir + "/3dParty/hunspell")
base.common_check_version("hunspell", "1", clean)
base.cmd("python", ["./before.py"])
if (build_js):

View File

@ -10,6 +10,8 @@ import glob
import icu_android
def fetch_icu(major, minor):
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")

View File

@ -0,0 +1,38 @@
#!/usr/bin/env python
import sys
sys.path.append('../..')
import config
import base
import os
import subprocess
def clear_module():
directories = ["glm", "libetonyek", "libodfgen", "librevenge", "mdds"]
for dir in directories:
if base.is_dir(dir):
base.delete_dir_with_access_error(dir)
def make(use_gperf = True):
old_cur_dir = os.getcwd()
print("[fetch & build]: iwork")
base_dir = base.get_script_dir() + "/../../core/Common/3dParty/apple"
os.chdir(base_dir)
base.check_module_version("3", clear_module)
os.chdir(old_cur_dir)
cmd_args = ["fetch.py"]
if use_gperf:
cmd_args.append("--gperf")
base.cmd_in_dir(base_dir, "python", cmd_args)
return
if __name__ == '__main__':
# manual compile
make(False)

View File

@ -119,7 +119,7 @@ def make():
# windows hack (delete later) ----------------------
if ("windows" == base.host_platform()):
base.delete_dir_with_access_error("v8/buildtools/win")
base.cmd("git", ["config", "--system", "core.longpaths", "true"])
base.cmd("git", ["config", "--system", "core.longpaths", "true"], True)
base.cmd("gclient", ["sync", "--force"], True)
else:
base.cmd("gclient", ["sync"], True)
@ -246,7 +246,7 @@ def make_xp():
base.cmd("./depot_tools/fetch", ["v8"], True)
base.cmd("./depot_tools/gclient", ["sync", "-r", "4.10.253"], True)
base.delete_dir_with_access_error("v8/buildtools/win")
base.cmd("git", ["config", "--system", "core.longpaths", "true"])
base.cmd("git", ["config", "--system", "core.longpaths", "true"], True)
base.cmd("gclient", ["sync", "--force"], True)
# save common py script

View File

@ -21,6 +21,12 @@ def change_bootstrap():
content += "@Subdir git\n"
content += "infra/3pp/tools/git/${platform} version:2@2.41.0.chromium.11\n"
base.replaceInFile("./depot_tools/bootstrap/bootstrap.py",
"raise subprocess.CalledProcessError(proc.returncode, argv, None)", "return")
base.replaceInFile("./depot_tools/bootstrap/bootstrap.py",
" _win_git_bootstrap_config()", " #_win_git_bootstrap_config()")
base.writeFile("./depot_tools/bootstrap/manifest.txt", content)
return
@ -105,6 +111,9 @@ def make():
if not base.is_dir(base_dir):
base.create_dir(base_dir)
if ("mac" == base.host_platform()):
base.cmd("git", ["config", "--global", "http.postBuffer", "157286400"], True)
os.chdir(base_dir)
if not base.is_dir("depot_tools"):
base.cmd("git", ["clone", "https://chromium.googlesource.com/chromium/tools/depot_tools.git"])
@ -121,7 +130,7 @@ def make():
base.copy_dir("./v8/third_party", "./v8/third_party_new")
if ("windows" == base.host_platform()):
os.chdir("v8")
base.cmd("git", ["config", "--system", "core.longpaths", "true"])
base.cmd("git", ["config", "--system", "core.longpaths", "true"], True)
os.chdir("../")
v8_branch_version = "remotes/branch-heads/8.9"
if ("mac" == base.host_platform()):
@ -137,6 +146,11 @@ def make():
else:
base.replaceInFile("depot_tools/gclient_paths.py", "@functools.lru_cache", "")
if ("mac" == base.host_platform()):
if not base.is_file("v8/build/config/compiler/BUILD.gn.bak"):
base.copy_file("v8/build/config/compiler/BUILD.gn", "v8/build/config/compiler/BUILD.gn.bak")
base.replaceInFile("v8/build/config/compiler/BUILD.gn", "\"-Wloop-analysis\",", "\"-Wloop-analysis\", \"-D_Float16=short\",")
if not base.is_file("v8/third_party/jinja2/tests.py.bak"):
base.copy_file("v8/third_party/jinja2/tests.py", "v8/third_party/jinja2/tests.py.bak")
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")

View File

@ -40,9 +40,10 @@ def make():
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, "HtmlFile2")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "HtmlRenderer")
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_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", root_dir + "/cmap.bin")
@ -76,9 +77,12 @@ def make():
if (0 == platform.find("win")):
base.copy_file(core_build_dir + "/lib/" + platform_postfix + "/doctrenderer.lib", root_dir + "/doctrenderer.lib")
base.copy_v8_files(core_dir, root_dir, platform, isWindowsXP)
# python wrapper
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "docbuilder.c")
base.copy_file(core_dir + "/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder.py", root_dir + "/docbuilder.py")
# java wrapper
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "docbuilder.jni")
base.copy_file(core_dir + "/DesktopEditor/doctrenderer/docbuilder.java/build/libs/docbuilder.jar", root_dir + "/docbuilder.jar")
# app
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, root_dir, "docbuilder")
@ -120,6 +124,25 @@ def make():
if (0 == platform.find("mac")):
base.mac_correct_rpath_x2t(root_dir)
base.mac_correct_rpath_docbuilder(root_dir)
base.create_x2t_js_cache(root_dir, "builder", platform)
# 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")
if 0 != platform.find("mac"):
delete_files(base.find_files(root_dir, "sdk-all.js"))
delete_files(base.find_files(root_dir, "sdk-all-min.js"))
base.delete_dir(root_dir + "/sdkjs/slide/themes")
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")
return

View File

@ -30,7 +30,6 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "kernel_network")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "graphics")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "doctrenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "HtmlRenderer")
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, "PdfFile")
@ -38,7 +37,10 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "UnicodeConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "Fb2File")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "EpubFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "IWorkFile")
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_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")
@ -61,13 +63,14 @@ def make():
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "ooxml_crypt")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "vboxtester")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "metafiletester")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "dictionariestester")
# js cache
base.generate_doctrenderer_config(archive_dir + "/DoctRenderer.config", "./", "builder", "", "./dictionaries")
base.create_x2t_js_cache(archive_dir, "core", platform)
base.delete_file(archive_dir + "/DoctRenderer.config")
# dictionaries
base.copy_dictionaries(git_dir + "/dictionaries", archive_dir + "/dictionaries", True, False)
if base.is_file(archive_dir + ".7z"):
base.delete_file(archive_dir + ".7z")
base.archive_folder(archive_dir + "/*", archive_dir + ".7z")
return

View File

@ -66,9 +66,10 @@ def make():
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", "HtmlFile2")
base.copy_lib(build_libraries_path, root_dir + "/converter", "HtmlRenderer")
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")
if ("ios" == platform):
@ -102,6 +103,7 @@ def make():
base.generate_doctrenderer_config(root_dir + "/converter/DoctRenderer.config", "../editors/", "desktop", "", "../dictionaries")
base.copy_dir(git_dir + "/document-templates/new", root_dir + "/converter/empty")
base.copy_dir(git_dir + "/desktop-apps/common/templates", root_dir + "/converter/templates")
# dictionaries
base.copy_dictionaries(git_dir + "/dictionaries", root_dir + "/dictionaries")
@ -182,6 +184,8 @@ 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")):
@ -213,8 +217,6 @@ def make():
base.create_dir(root_dir + "/editors")
base.copy_dir(base_dir + "/js/" + branding + "/desktop/sdkjs", root_dir + "/editors/sdkjs")
if len(os.listdir(root_dir + "/editors/sdkjs")) == 0:
base.delete_dir(root_dir + "/editors/sdkjs") # delete empty folder. for bug 62528
base.copy_dir(base_dir + "/js/" + branding + "/desktop/web-apps", root_dir + "/editors/web-apps")
for file in glob.glob(root_dir + "/editors/web-apps/apps/*/*/*.js.map"):
base.delete_file(file)
@ -260,6 +262,8 @@ def make():
if isUseJSC:
base.delete_file(root_dir + "/converter/icudtl.dat")
base.create_x2t_js_cache(root_dir + "/converter", "desktop", platform)
if (0 == platform.find("win")):
base.delete_file(root_dir + "/cef_sandbox.lib")
base.delete_file(root_dir + "/libcef.lib")

View File

@ -58,10 +58,11 @@ def make():
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, "HtmlFile2")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "HtmlRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "doctrenderer")
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_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", root_dir + "/cmap.bin")

View File

@ -78,10 +78,11 @@ def make():
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, "HtmlFile2")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "HtmlRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "doctrenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "Fb2File")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "EpubFile")
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_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")
@ -118,6 +119,8 @@ def make():
+ glob.glob(js_dir + "/web-apps/apps/*/mobile/dist/js/*.js.map"):
base.delete_file(file)
base.create_x2t_js_cache(converter_dir, "server", platform)
# 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"])

View File

@ -0,0 +1,104 @@
# This script was successfully executed on Ubuntu 22.04.5 LTS
# Before starting, make sure that:
# 1. Python >= 3.9
# 2. The current working folder with the script and its path do not contain spaces and use Latin characters.
# 3. Antivirus is turned off
# 4. There is enough free space on the disk (50GB Libre Office and during the unpacking of packages, it's recommended that you allocate at least 80 gigabytes of free space)
# 5. The current working folder with the script and its path do not contain spaces and use Latin characters.
# If the error "You must put some 'source' URIs in your sources.list" occurs, you need to run the command:
# software-properties-gtk
# in the terminal, and then under the "Ubuntu Software" tab, click "Source code" if it's not turned on and submit
# after completion, the file will appear:
# current_folder_with_script/libreoffice_build/instdir/soffice
# debugging can be done via MVS 2022
# https://wiki.documentfoundation.org/Development/IDE#Microsoft_Visual_Studio
# or via VS Code with c/c++ tools
# https://wiki.documentfoundation.org/Development/IDE#Visual_Studio_Code_(VSCode)
# or via Qt Creator
# https://wiki.documentfoundation.org/Development/IDE#Qt_Creator
# or via attatch to the soffice.bin process
# https://wiki.documentfoundation.org/Development/How_to_debug#Debugging_with_gdb
import subprocess
import sys
import os
CONFIGURE_PARAMS = [
"--enable-dbgutil",
"--without-doxygen",
"--enable-pch",
"--disable-ccache",
# "--with-visual-studio=2022",
'--enable-symbols="all"'
]
SUDO_DEPENDENCIES = [
"git", "build-essential", "zip", "ccache", "junit4", "libkrb5-dev", "nasm", "graphviz", "python3",
"python3-dev", "python3-setuptools", "qtbase5-dev", "libkf5coreaddons-dev", "libkf5i18n-dev",
"libkf5config-dev", "libkf5windowsystem-dev", "libkf5kio-dev", "libqt5x11extras5-dev", "autoconf",
"libcups2-dev", "libfontconfig1-dev", "gperf", "openjdk-17-jdk", "doxygen", "libxslt1-dev",
"xsltproc", "libxml2-utils", "libxrandr-dev", "libx11-dev", "bison", "flex", "libgtk-3-dev",
"libgstreamer-plugins-base1.0-dev", "libgstreamer1.0-dev", "ant", "ant-optional", "libnss3-dev",
"libavahi-client-dev", "libxt-dev"
]
DIR_NAME = "libreoffice"
OFFICE_PATH = "instdir/program/soffice"
class bcolors:
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
FAIL = '\033[91m'
RESET = '\033[0m'
def run_command(command, exit_on_error=True):
try:
subprocess.run(command, shell=True, check=True)
except subprocess.CalledProcessError as e:
print(f"{bcolors.FAIL}Error executing command: {command}{bcolors.RESET}")
if exit_on_error:
sys.exit(1)
def install_dependencies():
print("Updating package list...")
run_command("sudo apt update")
print("Adding PPA for GCC/G++ update...")
run_command("sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test")
run_command("sudo apt update")
print("Installing dependencies for LibreOffice...")
run_command("sudo apt-get build-dep -y libreoffice")
run_command(f"sudo apt-get install {' '.join(map(str, SUDO_DEPENDENCIES))}")
print("Updating GCC/G++ to v12...")
run_command("sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 60 --slave /usr/bin/g++ g++ /usr/bin/g++-12", exit_on_error=False)
print(bcolors.OKGREEN + "All dependencies successfully installed!" + bcolors.RESET)
def build_libreoffice():
print("Cloning LibreOffice repository...")
run_command(f"git clone https://git.libreoffice.org/core {DIR_NAME}", exit_on_error=False)
print("Changing to build directory...")
os.chdir(f"./{DIR_NAME}")
print("Start configurator autogen.sh...")
run_command(f"./autogen.sh {' '.join(map(str, CONFIGURE_PARAMS))}")
print(bcolors.OKCYAN + "Starting libreoffice build, this may take up to 24 hours and takes up about 20 GB of drive space. You will also most likely need at least 8 GBs of RAM, otherwise the machine might fall into swap and appear to freeze up..." + bcolors.RESET)
run_command("make")
print(bcolors.OKGREEN + "LibreOffice build completed!" + bcolors.RESET)
# print(bcolors.OKCYAN + "Running LibreOffice..." + bcolors.RESET)
# run_command(OFFICE_PATH, exit_on_error=False)
if __name__ == "__main__":
install_dependencies()
build_libreoffice()

View File

@ -0,0 +1,202 @@
# Before starting, make sure that:
# 1. MVS 2022 is installed and the necessary individual components are in its installer
# <20> Windows Universal C Runtime
# <20> .NET Framework 4.x SDK (.NET Framework 5.x SDK and later are currently not supported. These don't register their information to registry, don't have csc.exe and they use dotnet command with csc.dll instead for compiling.)
# <20> C++ 20xx Redistributable MSMs (only required to build MSI installer)
# <20> C++ Clang Compiler for Windows (x.x.x)
# 2. Java JDK >= 17
# 3. Antivirus is turned off
# 4. There is enough free space on the disk (50GB Libre Office, 50Gb cygwin64)
# after completion, the files will appear:
# {LO_BUILD_PATH}/sources/libo-core/instdir/program/soffice.exe
# {LO_BUILD_PATH}/sources/libo-core/LibreOffice.sln
# debugging can be done via MVS 2022
# https://wiki.documentfoundation.org/Development/IDE#Microsoft_Visual_Studio
# or via attatch to the soffice.bin process
# https://wiki.documentfoundation.org/Development/How_to_debug#Debugging_with_gdb
import sys
sys.path.append('../../scripts')
import threading
import os
import subprocess
import shutil
import argparse
import base
CYGWIN_DOWNLOAD_URL = 'https://cygwin.com/setup-x86_64.exe'
CYGWIN_TEMP_PATH = './tmp'
CYGWIN_SETUP_FILENAME = 'setup-x86_64.exe'
CYGWIN_SETUP_PARAMS = [
"-P", "autoconf",
"-P", "automake",
"-P", "bison",
"-P", "cabextract",
"-P", "doxygen",
"-P", "flex",
"-P", "gawk=5.2.2-1",
"-P", "gcc-g++",
"-P", "gettext-devel",
"-P", "git",
"-P", "gnupg",
"-P", "gperf",
"-P", "make",
"-P", "mintty",
"-P", "nasm",
"-P", "openssh",
"-P", "openssl",
"-P", "patch",
"-P", "perl",
"-P", "python",
"-P", "python3",
"-P", "pkg-config",
"-P", "rsync",
"-P", "unzip",
"-P", "vim",
"-P", "wget",
"-P", "zip",
"-P", "perl-Archive-Zip",
"-P", "perl-Font-TTF",
"-P", "perl-IO-String",
"--no-admin",
"--quiet-mode"
]
CYGWIN_BAT_PATH = 'C:/cygwin64/Cygwin.bat'
LO_BUILD_PATH = os.path.normpath(os.path.join(os.getcwd(), '../../../LO'))
CONFIGURE_PARAMS = [f'--with-external-tar="{LO_BUILD_PATH}/sources/lo-externalsrc"',
f'--with-junit="{LO_BUILD_PATH}/sources/junit-4.10.jar"',
f'--with-ant-home="{LO_BUILD_PATH}/sources/apache-ant-1.9.5"',
"--enable-pch",
"--disable-ccache",
"--with-visual-studio=2022",
"--enable-dbgutil",
'--enable-symbols="all"']
def create_folder_safe(folder_path):
if not os.path.exists(folder_path):
try:
os.mkdir(folder_path)
print(f"Folder '{folder_path}' created successfully.")
except Exception as e:
print(f"Error creating folder: {e}")
else:
print(f"Folder '{folder_path}' already exists.")
class CygwinRunner:
@staticmethod
def process_commands(commands: list[str]):
proc = subprocess.Popen(
[CYGWIN_BAT_PATH], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
shell=True, creationflags=subprocess.CREATE_NEW_CONSOLE
)
def read_stdout():
for line in iter(proc.stdout.readline, ''):
sys.stdout.write(line)
proc.stdout.close()
def read_stderr():
for line in iter(proc.stderr.readline, ''):
sys.stderr.write(line)
proc.stderr.close()
stdout_thread = threading.Thread(target=read_stdout)
stderr_thread = threading.Thread(target=read_stderr)
stdout_thread.start()
stderr_thread.start()
for command in commands:
proc.stdin.write(command + '\n')
proc.stdin.flush()
stdout_thread.join()
stderr_thread.join()
proc.stdin.close()
proc.wait()
@staticmethod
def install_gnu_make():
base.print_info("install_gnu_make")
commands = ['mkdir -p /opt/lo/bin',
'cd /opt/lo/bin',
'wget https://dev-www.libreoffice.org/bin/cygwin/make-4.2.1-msvc.exe',
'cp make-4.2.1-msvc.exe make',
'chmod +x make',
'exit']
CygwinRunner.process_commands(commands)
@staticmethod
def install_ant_and_junit():
base.print_info("install_ant_and_junit")
commands = [f'mkdir -p {LO_BUILD_PATH}/sources',
f'cd {LO_BUILD_PATH}/sources',
'wget https://archive.apache.org/dist/ant/binaries/apache-ant-1.9.5-bin.tar.bz2',
'tar -xjvf apache-ant-1.9.5-bin.tar.bz2',
'wget http://downloads.sourceforge.net/project/junit/junit/4.10/junit-4.10.jar',
'exit']
CygwinRunner.process_commands(commands)
@staticmethod
def clone_lo():
base.print_info("clone_lo")
commands = [f'cd {LO_BUILD_PATH}/sources',
'git clone https://gerrit.libreoffice.org/core libo-core',
'exit']
CygwinRunner.process_commands(commands)
@staticmethod
def build_autogen():
base.print_info("build_autogen")
commands = [f'cd {LO_BUILD_PATH}/sources/libo-core',
f"./autogen.sh {' '.join(map(str, CONFIGURE_PARAMS))}",
'exit']
CygwinRunner.process_commands(commands)
@staticmethod
def run_make_build():
base.print_info("run_make")
commands = [f'cd {LO_BUILD_PATH}/sources/libo-core',
f'/opt/lo/bin/make gb_COLOR=1',
"exit"]
CygwinRunner.process_commands(commands)
@staticmethod
def build_vs_integration():
base.print_info("run_make")
commands = [f'cd {LO_BUILD_PATH}/sources/libo-core',
f'/opt/lo/bin/make gb_COLOR=1 vs-ide-integration',
"exit"]
CygwinRunner.process_commands(commands)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="options")
parser.add_argument("--lo_build_path", dest="build_path", default=f'../../../LO')
parser.add_argument("--disable_sln", dest="disable_sln", action=argparse.BooleanOptionalAction)
args = parser.parse_args()
LO_BUILD_PATH = args.build_path
DISABLE_SLN = args.disable_sln
create_folder_safe(f'{LO_BUILD_PATH}/sources/lo-externalsrc')
create_folder_safe(CYGWIN_TEMP_PATH)
os.chdir(CYGWIN_TEMP_PATH)
base.download(CYGWIN_DOWNLOAD_URL, CYGWIN_SETUP_FILENAME)
subprocess.run([CYGWIN_SETUP_FILENAME] + CYGWIN_SETUP_PARAMS)
os.chdir('..')
shutil.rmtree(CYGWIN_TEMP_PATH)
CygwinRunner.install_gnu_make()
CygwinRunner.install_ant_and_junit()
CygwinRunner.clone_lo()
CygwinRunner.build_autogen()
CygwinRunner.run_make_build()
if not DISABLE_SLN:
CygwinRunner.build_vs_integration()

View File

@ -5,9 +5,6 @@ import base
import os
import json
def get_core_url(arch, branch):
return "http://repo-doc-onlyoffice-com.s3.amazonaws.com/" + base.host_platform() + "/core/" + branch + "/latest/" + arch + "/core.7z"
def make():
git_dir = base.get_script_dir() + "/../.."
old_cur = os.getcwd()
@ -18,16 +15,10 @@ def make():
os.chdir(work_dir)
arch = "x64"
arch2 = "_64"
if ("windows" == base.host_platform()) and not base.host_platform_is64():
arch = "x86"
arch2 = "_32"
url = get_core_url(arch, config.option("branch"))
url = base.get_autobuild_version("core", "", config.option("branch"))
data_url = base.get_file_last_modified_url(url)
if (data_url == "" and config.option("branch") != "develop"):
url = get_core_url(arch, "develop")
url = base.get_autobuild_version("core", "", "develop")
data_url = base.get_file_last_modified_url(url)
old_data_url = base.readFile("./core.7z.data")
@ -49,12 +40,6 @@ def make():
base.extract("./core.7z", "./")
base.writeFile("./core.7z.data", data_url)
platform = ""
if ("windows" == base.host_platform()):
platform = "win" + arch2
else:
platform = base.host_platform() + arch2
base.copy_files("./core/*", "./")
else:
print("-----------------------------------------------------------")

View File

@ -539,7 +539,9 @@ def check_mysqlServer():
print(mysql_full_name + 'configuration is valid')
dependence.sqlPath = info['Location']
return dependence
print(mysql_full_name + 'configuration is not valid')
print(mysql_full_name + 'configuration is not valid:' + connectionResult)
# if path exists, then further removal and installation fails(according to startup statistics). it is better to fix issue manually.
return dependence
print('Valid MySQL Server not found')
dependence.append_uninstall('MySQL Server')

View File

@ -6,6 +6,7 @@ import base
import shutil
import optparse
import dependence
import config
arguments = sys.argv[1:]
@ -17,6 +18,10 @@ parser.add_option("--remove-path", action="append", type="string", dest="remove-
(options, args) = parser.parse_args(arguments)
configOptions = vars(options)
# parse configuration
config.parse()
config.parse_defaults()
for item in configOptions["uninstall"]:
dependence.uninstallProgram(item)
for item in configOptions["remove-path"]:

View File

@ -8,6 +8,13 @@ import dependence
import traceback
import develop
# if (sys.version_info[0] >= 3):
# unicode = str
# host_platform = base.host_platform()
# if (host_platform == 'windows'):
# import libwindows
base_dir = base.get_script_dir(__file__)
def install_module(path):
@ -21,11 +28,18 @@ def find_rabbitmqctl(base_path):
return base.find_file(os.path.join(base_path, 'RabbitMQ Server'), 'rabbitmqctl.bat')
def restart_win_rabbit():
# todo maybe restarting is not relevant after many years and versions?
base.print_info('restart RabbitMQ node to prevent "Erl.exe high CPU usage every Monday morning on Windows" https://groups.google.com/forum/#!topic/rabbitmq-users/myl74gsYyYg')
rabbitmqctl = find_rabbitmqctl(os.environ['PROGRAMW6432']) or find_rabbitmqctl(os.environ['ProgramFiles(x86)'])
if rabbitmqctl is not None:
base.cmd_in_dir(base.get_script_dir(rabbitmqctl), 'rabbitmqctl.bat', ['stop_app'])
base.cmd_in_dir(base.get_script_dir(rabbitmqctl), 'rabbitmqctl.bat', ['start_app'])
try:
# code = libwindows.sudo(unicode(sys.executable), ['net', 'stop', 'rabbitmq'])
# code = libwindows.sudo(unicode(sys.executable), ['net', 'start', 'rabbitmq'])
base.cmd_in_dir(base.get_script_dir(rabbitmqctl), 'rabbitmqctl.bat', ['stop_app'])
base.cmd_in_dir(base.get_script_dir(rabbitmqctl), 'rabbitmqctl.bat', ['start_app'])
except SystemExit:
base.print_error('Perhaps Erlang cookies are different: Replace %userprofile%/.erlang.cookie with %WINDIR%/System32/config/systemprofile/.erlang.cookie')
raise
else:
base.print_info('Missing rabbitmqctl.bat')

View File

@ -21,14 +21,15 @@
"core/DesktopEditor/raster/JBig2",
"core/DesktopEditor/raster/Jp2",
"core/DesktopEditor/xml/libxml2",
"core/DesktopEditor/xmlsec",
"core/DesktopEditor/xmlsec",
"core/DjVuFile/libdjvu",
"core/DjVuFile/wasm",
"core/EpubFile",
"core/OOXML/PPTXFormat/Limit/pri",
"core/Fb2File",
"core/HtmlFile2",
"core/HtmlFile2",
"core/Apple",
"core/HwpFile",
"core/OdfFile/Common/utf8cpp",
"core/OfficeUtils/js/emsdk",
"core/OfficeUtils/src/zlib-1.2.11",
@ -197,6 +198,11 @@
"editors-ios/Vendor/ThreadSafeMutable/ThreadSafeMutableDictionary.h",
"editors-ios/Vendor/ThreadSafeMutable/ThreadSafeMutableDictionary.m"
]
},
{
"dir": "editors-webview-ios",
"fileExtensions": [".swift", ".xcconfig"],
"licensePath": "header.license",
}
]
}

View File

@ -1,5 +1,5 @@
/*
* (c) Copyright Ascensio System SIA 2010-2024
* (c) Copyright Ascensio System SIA 2010-2025
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)

View File

@ -43,7 +43,7 @@ if utils.is_linux():
},
{
"make": "rpm",
"src": "rpm/builddir/RPMS/*/*.rpm",
"src": "rpm/build/RPMS/*/*.rpm",
"dst": "builder/linux/rhel/"
}
]

View File

@ -7,14 +7,17 @@ import package_branding as branding
def make():
utils.log_h1("BUILDER")
if not (utils.is_windows() or utils.is_macos() or utils.is_linux()):
utils.log("Unsupported host OS")
return
if common.deploy:
make_archive()
if utils.is_windows():
make_windows()
elif utils.is_macos():
make_macos()
elif utils.is_linux():
make_linux()
else:
utils.log("Unsupported host OS")
return
def s3_upload(files, dst):
@ -24,111 +27,133 @@ def s3_upload(files, dst):
key = dst + utils.get_basename(f) if dst.endswith("/") else dst
upload = utils.s3_upload(f, "s3://" + branding.s3_bucket + "/" + key)
if upload:
utils.add_deploy_data(key)
utils.log("URL: " + branding.s3_base_url + "/" + key)
ret &= upload
return ret
def make_windows():
global inno_file, zip_file, suffix, key_prefix
utils.set_cwd("document-builder-package")
def make_archive():
utils.set_cwd(utils.get_path(
"build_tools/out/" + common.prefix + "/" + branding.company_name.lower()))
prefix = common.platformPrefixes[common.platform]
company = branding.company_name
product = branding.builder_product_name.replace(" ","")
source_dir = "..\\build_tools\\out\\%s\\%s\\%s" % (prefix, company, product)
package_name = company + "-" + product
package_version = common.version + "." + common.build
suffix = {
"windows_x64": "x64",
"windows_x86": "x86"
}[common.platform]
zip_file = "%s-%s-%s-%s.zip" % (company, product, package_version, suffix)
inno_file = "%s-%s-%s-%s.exe" % (company, product, package_version, suffix)
utils.log_h2("builder archive build")
utils.delete_file("builder.7z")
args = ["7z", "a", "-y", "builder.7z", "./documentbuilder/*"]
if utils.is_windows():
ret = utils.cmd(*args, verbose=True)
else:
ret = utils.sh(" ".join(args), verbose=True)
utils.set_summary("builder archive build", ret)
if common.clean:
utils.log_h2("builder clean")
utils.delete_dir("build")
utils.log_h2("copy arifacts")
utils.create_dir("build\\app")
utils.copy_dir_content(source_dir, "build\\app\\")
make_zip()
make_inno()
utils.log_h2("builder archive deploy")
dest = "builder-" + common.prefix.replace("_","-") + ".7z"
dest_latest = "archive/%s/latest/%s" % (common.branch, dest)
dest_version = "archive/%s/%s/%s" % (common.branch, common.build, dest)
ret = utils.s3_upload(
"builder.7z", "s3://" + branding.s3_bucket + "/" + dest_version)
utils.set_summary("builder archive deploy", ret)
if ret:
utils.log("URL: " + branding.s3_base_url + "/" + dest_version)
utils.s3_copy(
"s3://" + branding.s3_bucket + "/" + dest_version,
"s3://" + branding.s3_bucket + "/" + dest_latest)
utils.log("URL: " + branding.s3_base_url + "/" + dest_latest)
utils.set_cwd(common.workspace_dir)
return
def make_zip():
utils.log_h2("builder zip build")
utils.log_h3(zip_file)
def make_windows():
global package_version, arch
utils.set_cwd("document-builder-package")
ret = utils.cmd("7z", "a", "-y", zip_file, ".\\app\\*",
chdir="build", creates="build\\" + zip_file, verbose=True)
package_version = common.version + "." + common.build
arch = {
"windows_x64": "x64",
"windows_x86": "x86"
}[common.platform]
if common.clean:
utils.log_h2("builder clean")
utils.delete_dir("build")
utils.delete_files("exe\\*.exe")
utils.delete_files("zip\\*.msi")
if make_prepare():
make_zip()
make_inno()
else:
utils.set_summary("builder zip build", False)
utils.set_summary("builder inno build", False)
utils.set_cwd(common.workspace_dir)
return
def make_prepare():
args = [
"-Version", package_version,
"-Arch", arch
]
if common.sign:
args += ["-Sign"]
utils.log_h2("builder prepare")
ret = utils.ps1("make.ps1", args, verbose=True)
utils.set_summary("builder prepare", ret)
return ret
def make_zip():
args = [
"-Version", package_version,
"-Arch", arch
]
# if common.sign:
# args += ["-Sign"]
utils.log_h2("builder zip build")
ret = utils.ps1("make_zip.ps1", args, verbose=True)
utils.set_summary("builder zip build", ret)
if common.deploy and ret:
utils.log_h2("builder zip deploy")
ret = s3_upload(["build\\" + zip_file], "builder/win/generic/")
ret = s3_upload(utils.glob_path("zip/*.zip"), "builder/win/generic/")
utils.set_summary("builder zip deploy", ret)
return
def make_inno():
utils.log_h2("builder inno build")
utils.log_h3(inno_file)
args = [
"-Arch", suffix,
"-Version", common.version,
"-Build", common.build
"-Version", package_version,
"-Arch", arch
]
if not branding.onlyoffice:
args += [
"-Branding", "%s\\%s\\document-builder-package\\exe" % (common.workspace_dir, common.branding)
]
args += ["-Branding", common.branding]
if common.sign:
args += [
"-Sign",
"-CertName", branding.cert_name
]
ret = utils.ps1(
"make_inno.ps1", args, creates="build\\" + inno_file, verbose=True
)
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(["build\\" + inno_file], "builder/win/inno/")
ret = s3_upload(utils.glob_path("exe/*.exe"), "builder/win/inno/")
utils.set_summary("builder inno deploy", ret)
return
def make_macos():
company = branding.company_name.lower()
product = branding.builder_product_name.replace(" ","").lower()
source_dir = "build_tools/out/%s/%s/%s" % (common.prefix, company, product)
arch_list = {
"darwin_x86_64": "x86_64",
"darwin_arm64": "arm64"
}
suffix = arch_list[common.platform]
builder_tar = "../%s-%s-%s-%s-%s.tar.xz" % \
(company, product, common.version, common.build, suffix)
utils.set_cwd("document-builder-package")
utils.set_cwd(source_dir)
utils.log_h2("builder tar build")
make_args = ["tar"]
if common.platform == "darwin_arm64":
make_args += ["-e", "UNAME_M=arm64"]
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 tar build", ret)
if common.clean:
utils.log_h2("builder clean")
utils.delete_files("../*.tar*")
utils.log_h2("builder build")
ret = utils.sh("tar --xz -cvf %s *" % builder_tar, creates=builder_tar, verbose=True)
utils.set_summary("builder build", ret)
if common.deploy and ret:
utils.log_h2("builder deploy")
ret = s3_upload([builder_tar], "builder/mac/generic/")
utils.set_summary("builder deploy", ret)
if common.deploy:
utils.log_h2("builder tar deploy")
ret = s3_upload(utils.glob_path("tar/*.tar.xz"), "builder/mac/generic/")
utils.set_summary("builder tar deploy", ret)
utils.set_cwd(common.workspace_dir)
return

View File

@ -10,47 +10,37 @@ def make():
utils.log("Unsupported host OS")
return
if common.deploy:
make_core()
make_archive()
return
def make_core():
prefix = common.platformPrefixes[common.platform]
company = branding.company_name.lower()
repos = {
"windows_x64": { "repo": "windows", "arch": "x64", "version": common.version + "." + common.build },
"windows_x86": { "repo": "windows", "arch": "x86", "version": common.version + "." + common.build },
"darwin_x86_64": { "repo": "mac", "arch": "x64", "version": common.version + "-" + common.build },
"darwin_arm64": { "repo": "mac", "arch": "arm", "version": common.version + "-" + common.build },
"linux_x86_64": { "repo": "linux", "arch": "x64", "version": common.version + "-" + common.build },
}
repo = repos[common.platform]
branch = utils.get_env("BRANCH_NAME")
core_7z = utils.get_path("build_tools/out/%s/%s/core.7z" % (prefix, company))
dest_version = "%s/core/%s/%s/%s" % (repo["repo"], branch, repo["version"], repo["arch"])
dest_latest = "%s/core/%s/%s/%s" % (repo["repo"], branch, "latest", repo["arch"])
def make_archive():
utils.set_cwd(utils.get_path(
"build_tools/out/" + common.prefix + "/" + branding.company_name.lower()))
if branch is None:
utils.log_err("BRANCH_NAME variable is undefined")
utils.set_summary("core deploy", False)
return
if not utils.is_file(core_7z):
utils.log_err("file not exist: " + core_7z)
utils.set_summary("core deploy", False)
return
utils.log_h2("core archive build")
utils.delete_file("core.7z")
args = ["7z", "a", "-y", "core.7z", "./core/*"]
if utils.is_windows():
ret = utils.cmd(*args, verbose=True)
else:
ret = utils.sh(" ".join(args), verbose=True)
utils.set_summary("core archive build", ret)
utils.log_h2("core deploy")
utils.log_h2("core archive deploy")
dest = "core-" + common.prefix.replace("_","-") + ".7z"
dest_latest = "archive/%s/latest/%s" % (common.branch, dest)
dest_version = "archive/%s/%s/%s" % (common.branch, common.build, dest)
ret = utils.s3_upload(
core_7z,
"s3://" + branding.s3_bucket + "/" + dest_version + "/core.7z")
"core.7z", "s3://" + branding.s3_bucket + "/" + dest_version)
utils.set_summary("core archive deploy", ret)
if ret:
utils.log("URL: " + branding.s3_base_url + "/" + dest_version + "/core.7z")
utils.add_deploy_data(dest_version + "/core.7z")
ret = utils.s3_sync(
"s3://" + branding.s3_bucket + "/" + dest_version + "/",
"s3://" + branding.s3_bucket + "/" + dest_latest + "/",
delete=True)
utils.log("URL: " + branding.s3_base_url + "/" + dest_latest + "/core.7z")
utils.set_summary("core deploy", ret)
utils.log("URL: " + branding.s3_base_url + "/" + dest_version)
utils.s3_copy(
"s3://" + branding.s3_bucket + "/" + dest_version,
"s3://" + branding.s3_bucket + "/" + dest_latest)
utils.log("URL: " + branding.s3_base_url + "/" + dest_latest)
utils.set_cwd(common.workspace_dir)
return
def deploy_closuremaps_sdkjs(license):
@ -74,7 +64,6 @@ def deploy_closuremaps_sdkjs(license):
ret &= upload
if upload:
utils.log("URL: " + branding.s3_base_url + "/" + key)
utils.add_deploy_data(key)
utils.set_summary("sdkjs closure maps %s deploy" % license, ret)
return
@ -100,6 +89,5 @@ def deploy_closuremaps_webapps(license):
ret &= upload
if upload:
utils.log("URL: " + branding.s3_base_url + "/" + key)
utils.add_deploy_data(key)
utils.set_summary("web-apps closure maps %s deploy" % license, ret)
return

View File

@ -26,7 +26,6 @@ def s3_upload(files, dst):
key = dst + utils.get_basename(f) if dst.endswith("/") else dst
upload = utils.s3_upload(f, "s3://" + branding.s3_bucket + "/" + key)
if upload:
utils.add_deploy_data(key)
utils.log("URL: " + branding.s3_base_url + "/" + key)
ret &= upload
return ret
@ -64,6 +63,7 @@ def make_windows():
make_zip()
make_inno()
make_advinst()
make_online()
utils.set_cwd(common.workspace_dir)
return
@ -154,17 +154,6 @@ def make_inno():
else:
ret = False
utils.set_summary("desktop inno update deploy", ret)
changes_dir = common.workspace_dir + "\\" \
+ utils.get_path(branding.desktop_changes_dir) + "\\" + common.version
if common.platform == "windows_x64" and \
common.deploy and \
utils.glob_path(changes_dir + "\\*.html"):
utils.log_h2("desktop changelog deploy")
ret = s3_upload(
utils.glob_path(changes_dir + "\\*.html"),
"desktop/win/update/%s/%s/" % (common.version, common.build))
utils.set_summary("desktop changelog deploy", ret)
return
def make_advinst():
@ -188,6 +177,19 @@ def make_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)
return
#
# macOS
#
@ -294,11 +296,17 @@ def make_sparkle_updates():
macos_zip = "build/" + zip_filename + ".zip"
utils.create_dir(updates_dir)
utils.copy_file(macos_zip, updates_dir)
utils.copy_dir_content(released_updates_dir, updates_dir, ".zip")
utils.sh(
"ls -1t " + released_updates_dir + "/*.zip" \
+ " | head -n 3" \
+ " | while read f; do cp -fv \"$f\" " + updates_dir + "/; done",
verbose=True)
for file in utils.glob_path(changes_dir + "/" + common.version + "/*.html"):
filename = utils.get_basename(file).replace("changes", zip_filename)
utils.copy_file(file, updates_dir + "/" + filename)
for ext in [".html", ".ru.html"]:
changes_src = changes_dir + "/" + common.version + "/changes" + ext
changes_dst = updates_dir + "/" + zip_filename + ext
if not utils.copy_file(changes_src, changes_dst):
utils.write_file(changes_dst, "<!DOCTYPE html>placeholder")
sparkle_base_url = "%s/%s/updates/" % (branding.sparkle_base_url, suffix)
ret = utils.sh(

View File

@ -31,7 +31,6 @@ def make_mobile():
key = "mobile/android/" + zip_file
ret = utils.s3_upload(zip_file, "s3://" + branding.s3_bucket + "/" + key)
if ret:
utils.add_deploy_data(key)
utils.log("URL: " + branding.s3_base_url + "/" + key)
utils.set_summary("mobile deploy", ret)

View File

@ -21,7 +21,6 @@ def s3_upload(files, dst):
key = dst + utils.get_basename(f) if dst.endswith("/") else dst
upload = utils.s3_upload(f, "s3://" + branding.s3_bucket + "/" + key)
if upload:
utils.add_deploy_data(key)
utils.log("URL: " + branding.s3_base_url + "/" + key)
ret &= upload
return ret
@ -37,10 +36,13 @@ def make_windows(edition):
utils.log_h2("server " + edition + " build")
ret = utils.cmd("make", "clean", verbose=True)
args = ["-e", "PRODUCT_NAME=" + product_name]
if edition == "prerequisites":
make_args = ["exe-pr"]
else:
make_args = ["exe", "-e", "PRODUCT_NAME=" + product_name]
if not branding.onlyoffice:
args += ["-e", "BRANDING_DIR=../" + common.branding + "/document-server-package"]
ret &= utils.cmd("make", "packages", *args, verbose=True)
make_args += ["-e", "BRANDING_DIR=../" + common.branding + "/document-server-package"]
ret &= utils.cmd("make", *make_args, verbose=True)
utils.set_summary("server " + edition + " build", ret)
if common.deploy and ret:

View File

@ -266,11 +266,6 @@ def set_summary(target, status):
common.summary.append({target: status})
return
def add_deploy_data(key):
with open(common.deploy_data, 'a+') as f:
f.write(key + "\n")
return
def cmd(*args, **kwargs):
if kwargs.get("verbose"):
log("- cmd:")
@ -385,15 +380,13 @@ def s3_upload(src, dst, **kwargs):
ret = sh(" ".join(args), verbose=True)
return ret
def s3_sync(src, dst, **kwargs):
def s3_copy(src, dst, **kwargs):
args = ["aws"]
if kwargs.get("endpoint_url"):
args += ["--endpoint-url", kwargs["endpoint_url"]]
args += ["s3", "sync", "--no-progress"]
args += ["s3", "cp", "--no-progress"]
if kwargs.get("acl"):
args += ["--acl", kwargs["acl"]]
if kwargs.get("delete") and kwargs["delete"]:
args += ["--delete"]
args += [src, dst]
if is_windows():
ret = cmd(*args, verbose=True)

View File

@ -29,7 +29,7 @@ def check_support_platform(platform):
return False
return True
def make(platform, project, qmake_config_addon=""):
def make(platform, project, qmake_config_addon="", is_no_errors=False):
# check platform
if not check_support_platform(platform):
print("THIS PLATFORM IS NOT SUPPORTED")
@ -93,6 +93,9 @@ def make(platform, project, qmake_config_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)
base.correct_makefile_after_qmake(platform, makefile)
if ("1" == config.option("clean")):
@ -100,7 +103,7 @@ def make(platform, project, qmake_config_addon=""):
base.cmd_and_return_cwd("make", distclean_params, True)
base.cmd(qmake_app, build_params)
base.correct_makefile_after_qmake(platform, makefile)
base.cmd_and_return_cwd("make", ["-f", makefile] + get_j_num())
base.cmd_and_return_cwd("make", ["-f", makefile] + get_j_num(), is_no_errors)
else:
config_params_array = base.qt_config_as_param(config_param)
config_params_string = ""
@ -123,7 +126,7 @@ def make(platform, project, qmake_config_addon=""):
if ("0" != config.option("multiprocess")):
qmake_bat.append("set CL=/MP")
qmake_bat.append("call nmake -f " + makefile)
base.run_as_bat(qmake_bat)
base.run_as_bat(qmake_bat, is_no_errors)
if (base.is_file(stash_file)):
base.delete_file(stash_file)

View File

@ -1,16 +1,22 @@
# Documentation Generation Guide
This guide explains how to generate documentation for Onlyoffice API using the provided Python scripts, `generate_docs_json.py` and `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/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.
## Prerequisites
## Requirements
1. **Node.js and npm**: Ensure you have Node.js and npm installed on your machine. You can download them from [Node.js official website](https://nodejs.org/).
```bash
Node.js v20 and above
Python v3.10 and above
```
2. **jsdoc**: The scripts use `jsdoc` to generate documentation. Install it using npm:
```bash
npm install
```
## Installation
```bash
git clone https://github.com/ONLYOFFICE/build_tools.git
cd build_tools/scripts/sdkjs_common/jsdoc
npm install
```
## Scripts Overview
@ -24,7 +30,19 @@ This script generates JSON documentation based on the `apiBuilder.js` files.
```
- **Parameters**:
- `output_path` (optional): The directory where the JSON documentation will be saved. If not specified, the default path is `Onlyoffice/sdkjs/deploy/api_builder/json`.
- `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`
@ -36,7 +54,7 @@ This script generates Markdown documentation from the `apiBuilder.js` files.
```
- **Parameters**:
- `output_path` (optional): The directory where the Markdown documentation will be saved. If not specified, the default path is `Onlyoffice/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/`.
## Example
@ -45,6 +63,11 @@ To generate JSON documentation with the default output path:
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

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

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

View File

@ -0,0 +1,85 @@
exports.handlers = {
processingComplete: function(e) {
const filteredDoclets = [];
function checkNullProps(oDoclet) {
for (let key of Object.keys(oDoclet)) {
if (oDoclet[key] == null) {
delete oDoclet[key];
}
if (typeof(oDoclet[key]) == "object") {
checkNullProps(oDoclet[key]);
}
}
}
for (let i = 0; i < e.doclets.length; i++) {
const doclet = e.doclets[i];
if (true == doclet.undocumented || doclet.kind == 'package') {
continue;
}
const filteredDoclet = {
comment: doclet.comment,
meta: doclet.meta ? {
lineno: doclet.meta.lineno,
columnno: doclet.meta.columnno
} : doclet.meta,
kind: doclet.kind,
since: doclet.since,
name: doclet.name,
type: doclet.type ? {
names: doclet.type.names,
parsedType: doclet.type.parsedType
} : doclet.type,
description: doclet.description,
memberof: doclet.memberof,
properties: doclet.properties ? doclet.properties.map(property => ({
type: property.type ? {
names: property.type.names,
parsedType: property.type.parsedType
} : property.type,
name: property.name,
description: property.description,
optional: property.optional,
defaultvalue: property.defaultvalue
})) : doclet.properties,
longname: doclet.longname,
scope: doclet.scope,
alias: doclet.alias,
params: doclet.params ? doclet.params.map(param => ({
type: param.type ? {
names: param.type.names,
parsedType: param.type.parsedType
} : param.type,
name: param.name,
description: param.description,
optional: param.optional,
defaultvalue: param.defaultvalue
})) : doclet.params,
returns: doclet.returns ? doclet.returns.map(returnObj => ({
type: {
names: returnObj.type.names,
parsedType: returnObj.type.parsedType
}
})) : doclet.returns,
see: doclet.see
};
checkNullProps(filteredDoclet)
filteredDoclets.push(filteredDoclet);
}
e.doclets.splice(0, e.doclets.length, ...filteredDoclets);
}
};

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/slide/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/word/api_plugins.js", "../../../../sdkjs-forms/apiPlugins.js"]
},
"plugins": ["./correct_doclets.js"],
"opts": {
"destination": "./out",
"recurse": true,
"encoding": "utf8"
},
"templates": {
"json": {
"pretty": true
}
}
}

View File

@ -3,13 +3,16 @@ import subprocess
import json
import argparse
import re
import platform
root = '../../../..'
# Configuration files
configs = [
"./config/word.json",
"./config/cell.json",
"./config/slide.json",
"./config/forms.json"
"./config/builder/word.json",
"./config/builder/cell.json",
"./config/builder/slide.json",
"./config/builder/forms.json"
]
editors_maps = {
@ -19,21 +22,18 @@ editors_maps = {
"forms": "CFE"
}
def generate(output_dir):
missing_examples_file = f'{output_dir}/missing_examples.txt'
def generate(output_dir, md=False):
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# Recreate missing_examples.txt file
with open(missing_examples_file, 'w', encoding='utf-8') as f:
f.write('')
# 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"set EDITOR={editors_maps[editor_name]} && npx jsdoc -c {config} -X > {output_file}"
command_set_env = "export"
if (platform.system().lower() == "windows"):
command_set_env = "set"
command = f"{command_set_env} EDITOR={editors_maps[editor_name]} && npx jsdoc -c {config} -X > {output_file}"
print(f"Generating {editor_name}.json: {command}")
subprocess.run(command, shell=True)
@ -51,9 +51,12 @@ def generate(output_dir):
if 'see' in doclet:
if doclet['see'] is not None:
if editor_name == 'forms':
file_path = '../../../../' + doclet['see'][0].replace('{Editor}', 'Word')
doclet['see'][0] = doclet['see'][0].replace('{Editor}', 'Word')
else:
file_path = '../../../../' + doclet['see'][0].replace('{Editor}', editor_name.title())
doclet['see'][0] = doclet['see'][0].replace('{Editor}', editor_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()
@ -67,18 +70,20 @@ def generate(output_dir):
comment = ''
code_content = example_content
# Format content for doclet['example']
doclet['example'] = remove_js_comments(comment) + "```js\n" + remove_builder_lines(code_content) + "\n```"
else:
# Record missing examples in missing_examples.txt
with open(missing_examples_file, 'a', encoding='utf-8') as missing_file:
missing_file.write(f"{file_path}\n")
if md == True:
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```'
# 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 completed.")
print("Documentation generation for builder completed.")
def remove_builder_lines(text):
lines = text.splitlines() # Split text into lines
@ -91,25 +96,6 @@ def remove_js_comments(text):
# Remove multi-line comments, leaving text after /*
text = re.sub(r'/\*\s*|\s*\*/', '', text, flags=re.DOTALL)
return text.strip()
def get_current_branch(path):
try:
# Navigate to the specified directory and get the current branch name
result = subprocess.run(
["git", "rev-parse", "--abbrev-ref", "HEAD"],
cwd=path,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
if result.returncode == 0:
return result.stdout.strip()
else:
print(f"Error: {result.stderr}")
return None
except Exception as e:
print(f"Exception: {e}")
return None
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate documentation")
@ -118,12 +104,7 @@ if __name__ == "__main__":
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default="../../../../document-builder-declarations/document-builder" # Default value
default=f"{root}/office-js-api-declarations/office-js-api"
)
args = parser.parse_args()
branch_name = get_current_branch("../../../../sdkjs")
if branch_name:
args.destination = f"{args.destination}/{branch_name}"
generate(args.destination)

View File

@ -13,6 +13,8 @@ editors = [
"forms"
]
missing_examples = []
def load_json(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
@ -22,22 +24,26 @@ def write_markdown_file(file_path, content):
md_file.write(content)
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)
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 opening <b> tag with **
# Replace <b> tags with markdown bold
string = re.sub(r'<b>', '**', string)
# Replace closing </b> tag with **
string = re.sub(r'</b>', '**', string)
# Note
return re.sub(r'<note>(.*?)</note>', r'💡 \1', string, flags=re.DOTALL)
# 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:
@ -53,41 +59,126 @@ def correct_default_value(value, enumerations, classes):
return generate_data_types_markdown([value], enumerations, classes)
def remove_line_breaks(string):
return re.sub(r'[\r\n]', '', 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='../../'):
param_types_md = ' &#124;'.join(types)
"""
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 " | ".
"""
for enum in enumerations:
if enum['name'] in types:
param_types_md = param_types_md.replace(enum['name'], f"[{enum['name']}]({root}Enumeration/{enum['name']}.md)")
for cls in classes:
if cls in types:
param_types_md = param_types_md.replace(cls, f"[{cls}]({root}{cls}/{cls}.md)")
# Convert each raw type from JSDoc to TS
converted = [convert_jsdoc_array_to_ts(t) for t in types] # e.g. ["Drawing[]", "Foo[]", ...]
def replace_with_links(match):
# 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()
base_type = element.split('.')[0] # Take only the first part before the dot, if any
if any(enum['name'] == base_type for enum in enumerations):
return f"<[{element}](../../Enumeration/{base_type}.md)>"
elif base_type in classes:
return f"<[{element}](../../{base_type}/{base_type}.md)>"
return f"<{element}>"
return f"&lt;{element}&gt;"
return re.sub(r'<([^<>]+)>', replace_with_links, param_types_md)
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 += 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"
return content
def generate_method_markdown(method, enumerations, classes):
# 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)
@ -98,14 +189,14 @@ def generate_method_markdown(method, enumerations, classes):
content = f"# {method_name}\n\n{description}\n\n"
# Syntax section
# Syntax
param_list = ', '.join([param['name'] for param in params]) if params else ''
content += f"## Syntax\n\nexpression.{method_name}({param_list});\n\n"
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"
@ -121,43 +212,49 @@ def generate_method_markdown(method, enumerations, classes):
else:
content += "This method doesn't have any parameters.\n"
# Returns
content += "\n## Returns\n\n"
if returns:
return_type = ', '.join(returns[0].get('type', {}).get('names', [])) if returns[0].get('type') else 'Unknown'
# Check for enumerations and classes in return type and add links if they exist
return_type_md = generate_data_types_markdown([return_type], enumerations, classes)
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, and remove comment symbols
comment, code = example.split('```js', 1)
comment = remove_js_comments(comment)
content += f"\n\n## Example\n\n{comment}\n\n```javascript\n{code.strip()}\n"
# 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 content
return escape_text_outside_code_blocks(content)
def generate_properties_markdown(properties, enumerations, classes, root='../../'):
if (properties is None):
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))
param_types_md = generate_data_types_markdown(prop['type']['names'], enumerations, classes, root)
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"
content += "\n"
return content
# Escape outside code blocks
return escape_text_outside_code_blocks(content)
def generate_enumeration_markdown(enumeration, enumerations, classes):
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)
@ -165,41 +262,67 @@ def generate_enumeration_markdown(enumeration, enumerations, classes):
content = f"# {enum_name}\n\n{description}\n\n"
if 'TypeUnion' == enumeration['type']['parsedType']['type']:
ptype = enumeration['type']['parsedType']
if ptype['type'] == 'TypeUnion':
enum_empty = True # is empty enum
content += "## Type\n\nEnumeration\n\n"
content += "## Values\n\n"
elements = enumeration['type']['parsedType']['elements']
for element in elements:
element_name = element['name'] if element['type'] != 'NullLiteral' else 'null'
# Check if element is in enumerations or classes before adding link
if any(enum['name'] == element_name for enum in enumerations):
content += f"- [{element_name}](../../Enumeration/{element_name}.md)\n"
elif element_name in classes:
content += f"- [{element_name}](../../{element_name}/{element_name}.md)\n"
else:
content += f"- {element_name}\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']
for t in types:
t = generate_data_types_markdown([t], enumerations, classes)
content += t + "\n\n"
t_md = generate_data_types_markdown(types, enumerations, classes)
content += t_md + "\n\n"
# Example
if example:
# Separate comment and code, and remove comment symbols
comment, code = example.split('```js', 1)
comment = remove_js_comments(comment)
content += f"\n\n## Example\n\n{comment}\n\n```javascript\n{code.strip()}\n"
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 content
return escape_text_outside_code_blocks(content)
def process_doclets(data, output_dir):
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':
@ -217,37 +340,55 @@ def process_doclets(data, output_dir):
# Process classes
for class_name, methods in classes.items():
class_dir = os.path.join(output_dir, class_name)
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)
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_content = generate_method_markdown(method, enumerations, classes)
write_markdown_file(os.path.join(methods_dir, f"{method['name']}.md"), method_content)
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(output_dir, 'Enumeration')
enum_dir = os.path.join(editor_dir, 'Enumeration')
os.makedirs(enum_dir, exist_ok=True)
for enum in enumerations:
enum_content = generate_enumeration_markdown(enum, enumerations, classes)
write_markdown_file(os.path.join(enum_dir, f"{enum['name']}.md"), enum_content)
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')
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")
os.makedirs(output_dir + f'/{editor_name.title()}', exist_ok=True)
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 + f'/{editor_name}')
process_doclets(data, output_dir, editor_name.title())
shutil.rmtree(output_dir + 'tmp_json')
print('Done')
@ -262,5 +403,7 @@ if __name__ == "__main__":
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

@ -0,0 +1,111 @@
import os
import subprocess
import json
import argparse
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"
]
root = '../../../..'
def generate(output_dir, md=False):
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 builder 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,467 @@
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

@ -0,0 +1,248 @@
import os
import json
import re
import shutil
import argparse
import generate_docs_json
from datetime import datetime
# Configuration files
editors = [
"word",
"cell",
"slide",
"forms"
]
root = '../../../..'
missing_examples = []
def load_json(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
def read_file_content(file_path):
try:
with open(file_path, encoding='utf-8') as f:
return f.read()
except Exception as e:
missing_examples.append(file_path)
# print(f"Failed to open file {file_path}: {e}")
return ""
def extract_js_comments_as_text(text):
# Extract single-line comments (after //)
single_line_comments = re.findall(r'^\s*//(.*)$', text, flags=re.MULTILINE)
# Extract multi-line comments (between /* and */)
multi_line_comments = re.findall(r'/\*(.*?)\*/', text, flags=re.DOTALL)
# Combine all found comments into a single list
all_comments = single_line_comments + multi_line_comments
# Join comments into a single text, separated by a space
return " ".join(comment.strip() for comment in all_comments if comment.strip())
def extract_examples_blocks(content: str):
blocks = []
current_block = {"comments": [], "code": []}
in_comment_section = True # Collect comments until code appears
current_comment_group = [] # Accumulate lines of the current comment
for line in content.splitlines():
stripped = line.strip()
if not stripped:
# Empty line
if in_comment_section and current_comment_group:
# Finish the current comment group
comment_text = " ".join(current_comment_group)
current_block["comments"].append(comment_text)
current_comment_group = []
elif not in_comment_section:
# Empty line in the code keep it as is
current_block["code"].append(line)
continue
if stripped.startswith("//"):
if in_comment_section:
# Remove comment marker and extra spaces
current_comment_group.append(extract_js_comments_as_text(stripped))
else:
# Comment after code starts finish the current block and start a new one
blocks.append({
"comments": current_block["comments"],
"code": "\n".join(current_block["code"]).rstrip()
})
current_block = {"comments": [], "code": []}
in_comment_section = True
# Start a new comment group with the current line
current_comment_group = [stripped[2:].strip()]
else:
# Code line
if in_comment_section:
if current_comment_group:
comment_text = " ".join(current_comment_group)
current_block["comments"].append(comment_text)
current_comment_group = []
in_comment_section = False
current_block["code"].append(line)
# Finalize any remaining comment group
if in_comment_section and current_comment_group:
comment_text = " ".join(current_comment_group)
current_block["comments"].append(comment_text)
# Save the last block if it's not empty
if current_block["comments"] or current_block["code"]:
blocks.append({
"comments": current_block["comments"],
"code": "\n".join(current_block["code"]).rstrip()
})
return blocks
def extract_examples_blocks_temp(content: str):
lines = content.splitlines()
comment_blocks = []
current_group = []
first_code_index = None
for i, line in enumerate(lines):
stripped = line.strip()
if not stripped:
if current_group:
comment_blocks.append(" ".join(current_group))
current_group = []
continue
if stripped.startswith("//"):
current_group.append(stripped[2:].strip())
else:
if current_group:
comment_blocks.append(" ".join(current_group))
current_group = []
first_code_index = i
break
code_part = ""
if first_code_index is not None:
code_part = "\n".join(lines[first_code_index:]).rstrip()
return [{"comments": comment_blocks, "code": code_part}]
def create_entry(system_message, user_message, assistant_message, model):
entry = {
"created_at": datetime.now().isoformat(" "),
"messages": [
{"role": "system", "content": system_message},
{"role": "user", "content": user_message},
{"role": "assistant", "content": assistant_message}
],
"recommended": False,
"upvoted": True
}
if model is not "":
entry["model"] = model
return entry
def process_doclets(doclets, output_entries, editor_name, model):
for doclet in doclets:
kind = doclet.get("kind", "").lower()
see = doclet.get("see", [])
# The "see" field must always be present
if not see:
continue
# Processing based on the "kind" value
if kind == "function":
method_name = doclet.get("name", "")
memberof = doclet.get("memberof", "")
# 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:
continue
# Read the content of the first file listed in the "see" field
content = read_file_content(f'{root}/{see[0]}')
if content == "":
continue
# now use only first block cause there is bad comments in examples
blocks = extract_examples_blocks_temp(content)
for block in blocks:
assistant_message = block['code']
# default entry
output_entries.append(create_entry(system_message, default_user_message, assistant_message, model))
# If the file content contains comments, create a separate entry for each one
for comment in block['comments']:
output_entries.append(create_entry(system_message, comment, assistant_message, model))
def generate(output_dir, model):
print('Generating documentation JSONL dataset...')
shutil.rmtree(output_dir, ignore_errors=True)
os.makedirs(output_dir)
generate_docs_json.generate(f'{output_dir}/tmp_json')
output_entries = []
output_filename = "dataset.jsonl"
for editor_name in editors:
input_file = os.path.join(f'{output_dir}/tmp_json', editor_name + ".json")
doclets = load_json(input_file)
process_doclets(doclets, output_entries, editor_name, model)
with open(f'{output_dir}/{output_filename}', "w", encoding="utf-8") as out_file:
for entry in output_entries:
out_file.write(json.dumps(entry, ensure_ascii=False) + "\n")
shutil.rmtree(f'{output_dir}/tmp_json')
print('Done')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate documentation JSONL dataset")
parser.add_argument(
"destination",
type=str,
help="Destination directory for the generated documentation",
nargs='?', # Indicates the argument is optional
default="../../../../office-js-api/dataset" # Default value
)
parser.add_argument(
"model",
type=str,
help="Type of model",
nargs='?', # Indicates the argument is optional
default="" # Default value
)
args = parser.parse_args()
generate(args.destination, args.model)
print("START_MISSING_EXAMPLES")
print(",".join(missing_examples))
print("END_MISSING_EXAMPLES")

View File

@ -0,0 +1,39 @@
import subprocess
def fetch_branches():
#Fetch all branches without tags from the remote.
subprocess.run(['git', 'fetch', '--no-tags', 'origin', '+refs/heads/*:refs/remotes/origin/*'], check=True)
def get_branches():
#Get list of branches in the repository."""
result = subprocess.run(['git', 'branch', '-r'], capture_output=True, text=True)
return [line.strip() for line in result.stdout.splitlines()]
def parse_version(version_str):
#Parse version string and return a tuple of integers (major, minor, patch).
try:
return tuple(map(int, version_str.lstrip('v').split('.')))
except ValueError:
return (0, 0, 0) # Default for non-parsable versions
def get_max_version_branch(branches):
#Find the branch with the highest version.
max_branch = None
max_version = (0, 0, 0)
for branch in branches:
parts = branch.split('/')
if len(parts) >= 2 and (parts[1] == 'hotfix' or parts[1] == 'release'):
version = parse_version(parts[2])
if version > max_version:
max_version = version
max_branch = parts
return max_branch
if __name__ == "__main__":
fetch_branches() # Fetch branches without tags
branches = get_branches()
max_version_branch = get_max_version_branch(branches)
if max_version_branch:
print('/'.join(max_version_branch[1:])) # Print only the branch name without origin

View File

@ -14,6 +14,15 @@ def is_exist_in_array(projects, proj):
return True
return False
def get_full_projects_list(json_data, list):
result = []
for rec in list:
if rec in json_data:
result += get_full_projects_list(json_data, json_data[rec])
else:
result.append(rec)
return result
def adjust_project_params(params):
ret_params = params
@ -86,13 +95,9 @@ def get_projects(pro_json_path, platform):
# check aliases to modules
records_src = data[module]
records = []
records = get_full_projects_list(data, records_src)
for rec in records_src:
if rec in data:
records += data[rec]
else:
records.append(rec)
#print(records)
for rec in records:
params = []
@ -159,6 +164,18 @@ def get_projects(pro_json_path, platform):
if is_append:
result.append(root_dir + record)
# delete duplicates
old_results = result
result = []
map_results = set()
for item in old_results:
proj = item.replace("\\", "/")
if proj in map_results:
continue
map_results.add(proj)
result.append(proj)
if is_log:
print(result)
return result

View File

@ -1,6 +1,10 @@
{
"root" : "../",
"spell" : [
"[win,linux,mac]core/Common/3dParty/hunspell/qt/hunspell.pro"
],
"core" : [
"core/Common/3dParty/cryptopp/project/cryptopp.pro",
@ -18,12 +22,13 @@
"core/HtmlFile2/HtmlFile2.pro",
"core/Fb2File/Fb2File.pro",
"core/EpubFile/CEpubFile.pro",
"core/HtmlRenderer/htmlrenderer.pro",
"core/HwpFile/HWPFile.pro",
"core/Apple/IWork.pro",
"core/DocxRenderer/DocxRenderer.pro",
"core/DesktopEditor/doctrenderer/doctrenderer.pro",
"core/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder_func_lib.pro",
"[!no_x2t]core/OOXML/Projects/Linux/DocxFormatLib/DocxFormatLib.pro",
"[!no_x2t]core/OOXML/Projects/Linux/PPTXFormatLib/PPTXFormatLib.pro",
@ -51,15 +56,19 @@
"[win,linux,mac,!linux_arm64]core/OfficeCryptReader/ooxml_crypt/ooxml_crypt.pro",
"spell",
"[win,linux,mac,!no_tests]core/DesktopEditor/vboxtester/vboxtester.pro",
"[win,linux,mac,!no_tests]core/Test/Applications/StandardTester/standardtester.pro",
"[win,linux,mac,!no_tests]core/Test/Applications/x2tTester/x2ttester.pro",
"[win,linux,mac,!no_tests]core/Test/Applications/MetafileTester/MetafileTester.pro"
"[win,linux,mac,!no_tests]core/Test/Applications/MetafileTester/MetafileTester.pro",
"[win,linux,mac,!no_tests]core/Common/3dParty/hunspell/test/test.pro"
],
"builder" : [
"core"
"core",
"core/DesktopEditor/doctrenderer/docbuilder.python/src/docbuilder_func_lib.pro"
],
"server" : [
@ -70,13 +79,8 @@
"[win,linux]desktop-sdk/ChromiumBasedEditors/videoplayerlib/videoplayerlib.pro"
],
"spell" : [
"[win,linux,mac]core/Common/3dParty/hunspell/qt/hunspell.pro"
],
"desktop" : [
"core",
"spell",
"multimedia",
"core/DesktopEditor/xmlsec/src/ooxmlsignature.pro",
@ -88,7 +92,8 @@
"[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,!win_xp]desktop-apps/win-linux/extras/update-daemon/UpdateDaemon.pro",
"[win_xp]desktop-apps/win-linux/extras/online-installer/OnlineInstaller.pro"
],
"mobile" : [

View File

@ -1,4 +0,0 @@
{
"browser" : "chrome",
"browserUrl" : "C:/Program Files/Google/Chrome/Application/chrome.exe"
}

View File

@ -1,4 +0,0 @@
{
"browser" : "firefox",
"browserUrl" : "C:/Program Files/Mozilla Firefox/firefox.exe"
}

View File

@ -1,9 +0,0 @@
#!/usr/bin/env python
import sys
sys.path.append('../../scripts')
import base
import os
os.environ["PUPPETEER_SKIP_CHROMIUM_DOWNLOAD"] = "true"
base.cmd("npm", ["i", "puppeteer"])

View File

@ -1,64 +0,0 @@
#!/usr/bin/env python
import sys
sys.path.append('../../scripts')
import base
import os
import glob
import json
def get_tests_in_dir(directory):
files = []
for file in glob.glob(directory + "/*.js"):
if base.is_file(file):
files.append(file)
elif is_dir(file):
files += get_tests_in_dir(file)
return files
params = sys.argv[1:]
if (0 == len(params)):
print("use: run.py path_to_config [path_to_test]")
exit(0)
config_path = params[0]
test_file = "./tests"
if (1 < len(params)):
test_file = params[1]
tests_array = [test_file]
if base.is_dir(test_file):
tests_array = get_tests_in_dir(test_file)
config_content = "{}"
with open(config_path, "r") as config_path_loader:
config_content = config_path_loader.read()
print(config_content)
config = json.loads(config_content)
os.environ["PUPPETEER_SKIP_CHROMIUM_DOWNLOAD"] = "true"
if "browser" in config:
print("browser: " + config["browser"])
os.environ["PUPPETEER_PRODUCT"] = config["browser"]
if "browserUrl" in config:
print("browserUrl: " + config["browserUrl"])
os.environ["PUPPETEER_EXECUTABLE_PATH"] = config["browserUrl"]
if not base.is_dir("./work_directory"):
base.create_dir("./work_directory")
base.create_dir("./work_directory/cache")
base.create_dir("./work_directory/downloads")
for test in tests_array:
print("run test: " + test)
run_file = test + ".runned.js"
base.copy_file("./tester.js", run_file)
test_content = base.readFile(test)
test_content = test_content.replace("await Tester.", "Tester.")
test_content = test_content.replace("Tester.", "await Tester.")
base.replaceInFile(run_file, "\"%%CODE%%\"", test_content)
base.cmd("node", [run_file])
base.delete_file(run_file)

View File

@ -1,171 +0,0 @@
const puppeteer = require('puppeteer')
const pathfs = require('path')
const fs = require('fs');
function TesterImpl()
{
this.browser = null;
this.page = null;
this.width = 1500;
this.height = 800;
this.pixelRatio = 1;
this.cacheDir = pathfs.resolve("./work_directory/cache");
this.downloadsDir = pathfs.resolve("./work_directory/downloads");
this.downloadCounter = 0;
this.load = async function(url)
{
const head = { x: 100, y: 200 };
this.browser = await puppeteer.launch({
headless: false,
product: process.env["PUPPETEER_PRODUCT"],
args: [
"--disable-infobars",
`--window-size=${this.width+head.x},${this.height+head.y}`,
"--disk-cache-dir=" + this.cacheDir
],
defaultViewport : {width: this.width, height: this.height, deviceScaleFactor : this.pixelRatio }
});
this.page = await this.browser.newPage();
await this.page.setViewport({ width: this.width, height: this.height });
let waitObject = (process.env["PUPPETEER_PRODUCT"] === "firefox") ? { waitUntil: "networkidle0", timeout: 15000 } : {};
await this.page.goto(url + "&autotest=enabled", waitObject);
console.log("[tester] pageLoaded");
return this.page;
};
this.close = async function(nosleep)
{
if (true !== nosleep)
await this.waitAutosave();
await this.browser.close();
};
this.sleep = async function(ms)
{
return await new Promise(resolve => setTimeout(resolve, ms));
};
this.waitEditor = async function()
{
// TODO: wait first onEndRecalculate
await this.sleep(5000);
console.log("[tester] editorReady");
};
this.waitAutosave = async function()
{
await this.sleep(5000);
};
this.evaluateInMainFrame = async function(code)
{
return await this.page.evaluate(code);
};
this.evaluateInEditorFrame = async function(code)
{
const frame = await this.page.frames().find(frame => frame.name() === 'frameEditor');
if (!frame)
return;
return await frame.evaluate(code);
};
this.click = async function(id)
{
let res = await this.evaluateInEditorFrame("document.getElementById(\"" + id + "\").click(); \"[tester] clicked: " + id + "\"");
//console.log(res);
await this.sleep(200);
return res;
};
this.mouseClick = async function(x, y, options)
{
let res = await this.page.mouse.click(x, y, options);
await this.sleep(200);
return res;
};
this.eval = async function(code)
{
let res = await this.evaluateInEditorFrame(code);
await this.sleep(200);
return res;
};
this.keyDown = async function(key)
{
// https://pptr.dev/api/puppeteer.keyinput
let res = await this.page.keyboard.down(key);
await this.sleep(200);
return res;
};
this.keyUp = async function(key)
{
// https://pptr.dev/api/puppeteer.keyinput
let res = await this.page.keyboard.up(key);
await this.sleep(200);
return res;
};
this.keyClick = async function(key)
{
// https://pptr.dev/api/puppeteer.keyinput
let res = await this.page.keyboard.down(key);
res = await this.page.keyboard.up(key);
await this.sleep(200);
return res;
};
this.keyPress = async function(key)
{
// https://pptr.dev/api/puppeteer.keyinput
let res = await this.page.keyboard.press(key);
await this.sleep(200);
return res;
};
this.input = async function(text)
{
let res = await this.page.keyboard.type(text);
await this.sleep(200);
return res;
};
this.downloadFile = async function(format, path)
{
const tmpDir = pathfs.resolve(this.downloadsDir, "./tmp" + this.downloadCounter++);
fs.mkdirSync(tmpDir);
// emulate download
const client = await this.page.target().createCDPSession();
await client.send("Page.setDownloadBehavior", {
behavior: "allow",
downloadPath: tmpDir
});
await this.evaluateInEditorFrame("document.querySelectorAll('[data-layout-name=\"toolbar-file\"]')[0].click();");
await this.sleep(200);
await this.evaluateInEditorFrame("document.getElementsByClassName(\"svg-format-" + format + "\")[0].click();");
await this.sleep(200);
await this.evaluateInEditorFrame("document.getElementById(\"fm-btn-return\").click();");
await this.sleep(2000);
const files = fs.readdirSync(tmpDir);
fs.copyFileSync(pathfs.resolve(tmpDir, "./" + files[0]), pathfs.resolve(path));
fs.rmSync(tmpDir, { recursive: true, force: true });
};
}
const Tester = new TesterImpl;
try {
(async () => {
"%%CODE%%"
})();
} catch (err) {
console.error(err);
}

View File

@ -1,27 +0,0 @@
Tester.load("path_to_file");
Tester.waitEditor();
// down Enter
Tester.keyClick("Enter");
// type text
Tester.input("Hello World!");
Tester.keyPress("ArrowLeft");
Tester.keyDown("Shift");
for (let i = 0; i < 5; i++)
Tester.keyPress("ArrowLeft");
Tester.keyUp("Shift");
// bold
Tester.click("id-toolbar-btn-bold");
// italic
Tester.mouseClick(115, 105);
// if needed
Tester.waitAutosave();
Tester.downloadFile("docx", "./work_directory/new.docx")
Tester.downloadFile("odt", "./work_directory/new.odt")
Tester.close(true);

View File

@ -0,0 +1,114 @@
#!/usr/bin/env python
import sys
sys.path.append('../../scripts')
import base
import os
import glob
import base64
sys.stdin.reconfigure(encoding='utf-8')
sys.stdout.reconfigure(encoding='utf-8')
base.configure_common_apps()
def change_property(data_src, name, value):
data = data_src
creator_open = "<dc:" + name + ">"
creator_close = "</dc:" + name + ">"
open_tag_pos = data.find(creator_open)
if open_tag_pos == -1:
creator_close_to_find = "<dc:" + name + "/>"
else:
creator_close_to_find = "</dc:" + name + ">"
close_tag_pos = data.find(creator_close_to_find)
last_tag_pos = data.find("</cp:coreProperties>")
if open_tag_pos != -1 and close_tag_pos != - 1:
data = data[:open_tag_pos + len(creator_open)] + value + data[close_tag_pos:]
elif close_tag_pos != - 1:
data = data[:close_tag_pos] + creator_open + value + creator_close + data[close_tag_pos + len(creator_close_to_find):]
else:
data = data[:last_tag_pos] + creator_open + value + creator_close + data[last_tag_pos:]
return data
def change_author_name(file_input):
temp_dir = os.getcwd().replace("\\", "/") + "/temp"
base.create_dir(temp_dir)
app = "7za" if ("mac" == base.host_platform()) else "7z"
base.cmd_exe(app, ["x", "-y", file_input, "-o" + temp_dir, "docProps/core.xml", "-r"])
with open(temp_dir + "/docProps/core.xml", 'r', encoding='utf-8') as file:
data = file.read()
data = change_property(data, "creator", "")
data = change_property(data, "lastModifiedBy", "")
with open(temp_dir + "/docProps/core.xml", 'w', encoding='utf-8') as file:
file.write(data)
base.cmd_exe(app, ["a", "-r", file_input, temp_dir + "/docProps"])
base.delete_dir(temp_dir)
def get_files(dir):
arr_files = []
for file in glob.glob(dir + "/*"):
if base.is_file(file):
arr_files.append(file)
elif base.is_dir(file):
arr_files += get_files(file)
return arr_files
def get_local_path(base, src_dir):
test1 = base.replace("\\", "/")
test2 = src_dir.replace("\\", "/")
return test2[len(test1)+1:]
params = sys.argv[1:]
if (3 > len(params)):
print("use: convert.py path_to_x2t_directory path_to_input_directory path_to_output_directory")
exit(0)
base.configure_common_apps()
x2t_directory = params[0]
src_directory = params[1]
dst_directory = params[2]
if base.is_dir(dst_directory):
base.delete_dir(dst_directory)
base.create_dir(dst_directory)
src_files = get_files(src_directory)
for file in src_files:
directory = os.path.dirname(file)
name = os.path.basename(file)
directory_out_file = dst_directory + "/" + get_local_path(src_directory, directory)
if not base.is_dir(directory_out_file):
os.makedirs(directory_out_file, exist_ok=True)
name_without_ext = os.path.splitext(name)[0]
name_ext = os.path.splitext(name)[1][1:]
dst_ext = name_ext
if ("docx" == name_ext) or ("dotx" == name_ext):
dst_ext = "dotx"
elif ("pptx" == name_ext) or ("potx" == name_ext):
dst_ext = "potx"
elif ("xlsx" == name_ext) or ("xltx" == name_ext):
dst_ext = "xltx"
dst_name = name_without_ext
if (len(dst_name) < 4) or (dst_name[0:4] != "[32]"):
dst_name = "[32]" + base64.b32encode(name_without_ext.encode("utf-8")).decode("utf-8")
dst_file = directory_out_file + "/" + dst_name + "." + dst_ext
os.makedirs(directory_out_file, exist_ok=True)
base.cmd_in_dir(x2t_directory, "x2t", [file, dst_file])
change_author_name(dst_file)
print(name_without_ext + " => " + dst_name)

View File

@ -55,14 +55,23 @@ def install_qt():
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
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")
return
if not base.is_file("./node_js_setup_14.x"):
print("install dependencies...")
deps.install_deps()
if not base.is_dir("./qt_build"):
if not base.is_dir("./qt_build"):
print("install qt...")
install_qt()
install_qt_prebuild()
branch = get_branch_name("../..")
@ -103,6 +112,3 @@ build_tools_params = ["--branch", branch,
base.cmd_in_dir("../..", "./configure.py", build_tools_params)
base.cmd_in_dir("../..", "./make.py")

View File

@ -39,9 +39,14 @@ def install_deps():
"libxi-dev",
"libxrender-dev",
"libxss1",
"libncurses5"]
"libncurses5",
"libncurses6",
"curl",
"libxkbcommon-dev",
"libxkbcommon-x11-dev"]
base.cmd("sudo", ["apt-get", "install", "-y"] + packages)
for package in packages:
base.cmd("sudo", ["apt-get", "install", "-y", package], True)
# nodejs
base.cmd("sudo", ["apt-get", "install", "-y", "nodejs"])
@ -79,4 +84,3 @@ def install_deps():
if __name__ == "__main__":
install_deps()

View File

@ -1 +1 @@
8.1.1
8.3.3