Compare commits

..

1 Commits

Author SHA1 Message Date
332cd6240b Add libaom to libheif build 2026-02-02 13:35:28 +03:00
35 changed files with 560 additions and 1237 deletions

View File

@ -1,4 +1,4 @@
FROM ubuntu:24.04
FROM ubuntu:20.04
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive
@ -9,37 +9,25 @@ RUN echo 'keyboard-configuration keyboard-configuration/layoutcode string us' |
echo 'keyboard-configuration keyboard-configuration/modelcode string pc105' | debconf-set-selections
RUN apt-get -y update && \
apt-get -y install sudo \
git \
git-lfs \
curl \
wget \
p7zip-full
apt-get -y install tar \
sudo \
wget
RUN wget https://github.com/Kitware/CMake/releases/download/v3.30.0/cmake-3.30.0-linux-x86_64.tar.gz && \
tar -xzf cmake-3.30.0-linux-x86_64.tar.gz -C /opt && \
ln -s /opt/cmake-3.30.0-linux-x86_64/bin/cmake /usr/local/bin/cmake && \
ln -s /opt/cmake-3.30.0-linux-x86_64/bin/ctest /usr/local/bin/ctest && \
rm cmake-3.30.0-linux-x86_64.tar.gz
ADD . /build_tools
WORKDIR /build_tools
# Install local Python
RUN cd tools/linux && \
./python.sh
RUN mkdir -p /opt/python3 && \
wget -P /opt/python3/ https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/python/python3.tar.gz && \
tar -xzf /opt/python3/python3.tar.gz -C /opt/python3 --strip-components=1
# Fetch Qt binaries
RUN cd tools/linux && \
./python3/bin/python3 ./qt_binary_fetch.py amd64
ENV PATH="/opt/python3/bin:${PATH}"
# Install system dependencies
RUN cd tools/linux && \
./python3/bin/python3 ./deps.py
RUN ln -s /opt/python3/bin/python3.10 /usr/bin/python
# Install CMake
RUN cd tools/linux && \
./cmake.sh
# Fetch sysroot
RUN cd tools/linux/sysroot && \
../python3/bin/python3 ./fetch.py amd64
ARG BRANCH=master
ENV BRANCH=${BRANCH}
CMD ["sh", "-c", "./tools/linux/python3/bin/python3 ./configure.py --sysroot \"1\" --clean \"0\" --update-light \"1\" --branch \"${BRANCH}\" --update \"1\" --module \"desktop server builder\" --qt-dir \"$(pwd)/tools/linux/qt_build/Qt-5.9.9\" && ./tools/linux/python3/bin/python3 ./make.py"]
CMD ["sh", "-c", "cd tools/linux && python3 ./automate.py"]

388
README.md
View File

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

View File

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

View File

@ -256,10 +256,7 @@ def delete_dir(path):
shutil.rmtree(get_path(path), ignore_errors=True)
return
def copy_lib(src_folder, dst, name, subdir=""):
src = src_folder
if subdir != "":
src += ("/" + subdir)
def copy_lib(src, dst, name):
if (config.check_option("config", "bundle_dylibs")) and is_dir(src + "/" + name + ".framework"):
copy_dir(src + "/" + name + ".framework", dst + "/" + name + ".framework", symlinks=True)
@ -446,14 +443,14 @@ def cmd_in_dir_qemu(platform, directory, prog, args=[], is_no_errors=False):
"default_libs": "/usr/arm-linux-gnueabi"
}
}
if platform not in platform_config:
return 0
libs_path = platform_config[platform]["default_libs"]
if config.option("sysroot") != "":
libs_path = config.option("sysroot_" + platform)
return cmd_in_dir(directory, platform_config[platform]["qemu"], ["-L", libs_path, prog] + args, is_no_errors)
def create_qemu_wrapper(binary_path, platform):
@ -461,19 +458,19 @@ def create_qemu_wrapper(binary_path, platform):
binary_name = os.path.basename(binary_path)
binary_bin = binary_path + '.bin'
sysroot = config.option("sysroot_" + platform)
if os.path.exists(binary_path):
os.rename(binary_path, binary_bin)
wrapper_content = f'''#!/bin/bash
DIR="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)"
export QEMU_LD_PREFIX={sysroot}
exec qemu-aarch64 -L {sysroot} "$DIR/{binary_name}.bin" "$@"
'''
with open(binary_path, 'w') as f:
f.write(wrapper_content)
os.chmod(binary_path, 0o755)
return binary_bin
@ -659,7 +656,6 @@ def get_repositories():
result.update(get_server_addons())
result["document-server-integration"] = [False, False]
result["document-templates"] = [False, False]
result["document-formats"] = [False, False]
get_branding_repositories(result)
return result
@ -966,7 +962,7 @@ def qt_copy_icu(out, platform):
postfixes += ["/aarch64-linux-gnu"]
elif ("linux_32" == platform):
postfixes += ["/i386-linux-gnu"]
for postfix in postfixes:
tests += [prefix + "/lib" + postfix]
tests += [prefix + "/lib64" + postfix]
@ -1365,10 +1361,7 @@ def mac_change_rpath_library(lib_name, old, new):
def mac_correct_rpath_binary(path, libs):
# if framework are built, instead of correcting lib paths add `@loader_path` to rpaths with `mac_add_loader_path_to_rpath()`
try:
if config.check_option("config", "bundle_dylibs"):
return
except:
if config.check_option("config", "bundle_dylibs"):
return
for lib in libs:
@ -1400,7 +1393,6 @@ def mac_correct_rpath_x2t(dir):
mac_correct_rpath_library("IWorkFile", ["UnicodeConverter", "kernel"])
mac_correct_rpath_library("HWPFile", ["UnicodeConverter", "kernel", "graphics", "StarMathConverter"])
mac_correct_rpath_library("StarMathConverter", ["kernel"])
mac_correct_rpath_library("ooxmlsignature", ["kernel"])
def correct_core_executable(name, libs):
cmd("chmod", ["-v", "+x", name])
@ -1408,7 +1400,7 @@ def mac_correct_rpath_x2t(dir):
mac_correct_rpath_binary(name, mac_icu_libs + libs)
return
correct_core_executable("x2t", ["UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "doctrenderer", "DocxRenderer", "IWorkFile", "HWPFile", "StarMathConverter", "ooxmlsignature"])
correct_core_executable("x2t", ["UnicodeConverter", "kernel", "kernel_network", "graphics", "PdfFile", "XpsFile", "OFDFile", "DjVuFile", "HtmlFile2", "Fb2File", "EpubFile", "doctrenderer", "DocxRenderer", "IWorkFile", "HWPFile", "StarMathConverter"])
if is_file("./allfontsgen"):
correct_core_executable("allfontsgen", ["UnicodeConverter", "kernel", "graphics"])
if is_file("./allthemesgen"):
@ -1641,12 +1633,6 @@ def hack_xcode_ios():
file.write(filedata)
return
def find_ios_sdk(sdk_name):
return run_command("xcrun --sdk " + sdk_name + " --show-sdk-path")['stdout']
def find_xcode_toolchain(sdk_name):
return run_command("xcrun --sdk " + sdk_name + " --show-toolchain-path")['stdout']
def find_mac_sdk_version():
sdk_dir = run_command("xcode-select -print-path")['stdout']
sdk_dir = os.path.join(sdk_dir, "Platforms/MacOSX.platform/Developer/SDKs")
@ -1959,7 +1945,7 @@ def set_sysroot_env(platform):
path = config.option("sysroot_" + platform)
sysroot_path_bin = config.get_custom_sysroot_bin(platform)
compiler_gcc_prefix = get_compiler_gcc_prefix(platform)
os.environ['PATH'] = sysroot_path_bin + ":" + get_env("PATH")
os.environ['LD_LIBRARY_PATH'] = config.get_custom_sysroot_lib(platform)
@ -1967,7 +1953,7 @@ def set_sysroot_env(platform):
os.environ['CXX'] = compiler_gcc_prefix + "g++"
os.environ['AR'] = compiler_gcc_prefix + "ar"
os.environ['RANLIB'] = compiler_gcc_prefix + "ranlib"
os.environ['CFLAGS'] = "--sysroot=" + path
os.environ['CXXFLAGS'] = "--sysroot=" + path
os.environ['LDFLAGS'] = "--sysroot=" + path
@ -1989,7 +1975,7 @@ def check_python():
download('https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/python/python3.tar.gz', directory + "/python3.tar.gz")
download('https://github.com/ONLYOFFICE-data/build_tools_data/raw/refs/heads/master/python/extract.sh', directory + "/extract.sh")
cmd_in_dir(directory, "chmod", ["+x", "./extract.sh"])
cmd_in_dir(directory, "./extract.sh")
cmd_in_dir(directory, "./extract.sh")
directory_bin = directory_bin.replace(" ", "\\ ")
os.environ["PATH"] = directory_bin + os.pathsep + os.environ["PATH"]
return

View File

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

View File

@ -10,6 +10,7 @@ import config
# libs versions
X265_VERSION = "4.1"
DE265_VERSION = "1.0.16"
AOM_VERSION = "3.12.0"
# 1.18.2 - the latest version of libheif supporting C++11 builds (as for now)
HEIF_VERSION = "1.18.2"
@ -72,10 +73,7 @@ def build_with_cmake(platform, cmake_args, build_type):
cmake_args_ext += ["-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0", "-DCMAKE_OSX_ARCHITECTURES=arm64"]
elif config.option("sysroot") != "":
# force use custom CXXFLAGS with Release/Debug build
if (platform == "linux_64"):
cmake_args += ["-DCMAKE_TOOLCHAIN_FILE=" + LINUX_CUSTOM_SYSROOT_TOOLCHAIN_FILE]
else:
cmake_args += ["-DCMAKE_TOOLCHAIN_FILE=" + LINUX_SYSTEM_AARCH64_TOOLCHAIN_FILE]
cmake_args += ["-DCMAKE_TOOLCHAIN_FILE=" + LINUX_CUSTOM_SYSROOT_TOOLCHAIN_FILE]
elif platform == "linux_arm64" and not base.is_os_arm():
cmake_args += ["-DCMAKE_TOOLCHAIN_FILE=" + LINUX_SYSTEM_AARCH64_TOOLCHAIN_FILE]
# IOS
@ -112,7 +110,7 @@ def build_with_cmake(platform, cmake_args, build_type):
# env setup for custom sysroot
if config.option("sysroot") != "":
base.set_sysroot_env(platform)
base.set_sysroot_env("linux_arm64")
# run cmake
base.cmd("cmake", cmake_args + cmake_args_ext)
@ -282,6 +280,52 @@ def make_de265(base_dir, build_type):
make_common(build_de265, cmake_args)
return
def make_aom(base_dir, build_type):
# fetch lib repo
if not base.is_dir("aom"):
fetch_repo("https://aomedia.googlesource.com/aom", f"v{AOM_VERSION}")
# prepare cmake args
cmake_dir = base_dir + "/aom"
cmake_args = [
cmake_dir,
"-DCMAKE_BUILD_TYPE=" + build_type,
"-DBUILD_SHARED_LIBS=OFF", # do not build shared libs
"-DENABLE_SDL=OFF", # disable SDL
"-DENABLE_DECODER=ON", # enable AOM V1 decoder (for AVIF image format)
"-DENABLE_ENCODER=ON", # enable AOM V1 encoder (for AVIF image format)
"-DENABLE_TESTS=OFF", # do not build tests
"-DENABLE_DOCS=OFF", # do not build docs
"-DENABLE_EXAMPLES=OFF", # do not build examples
"-DENABLE_TOOLS=OFF", # do not build lib tools
]
# lib build function
def build_aom(platform, cmake_args):
# check if target lib has already been built
build_dir = get_build_dir(base_dir, "aom", platform, build_type)
if platform.find("win") != -1:
target_lib = os.path.join(build_dir, build_type, "aom.lib")
else:
target_lib = os.path.join(build_dir, "aom.a")
if base.is_file(target_lib):
return
# go to the build directory
base.create_dir(build_dir)
os.chdir(build_dir)
# run build
build_with_cmake(platform, cmake_args, build_type)
# for ios copy target library from the default build path
if platform.find("ios") != -1:
xcode_sdk = get_xcode_sdk(platform)
base.copy_file(f"/{build_type}-{xcode_sdk}/aom.a", build_dir)
# reset directory
os.chdir(base_dir)
return
make_common(build_aom, cmake_args)
return
def make_heif(base_dir, build_type):
# fetch lib repo
if not base.is_dir("libheif"):
@ -306,8 +350,8 @@ def make_heif(base_dir, build_type):
"-DCMAKE_BUILD_TYPE=" + build_type,
"-DBUILD_SHARED_LIBS=OFF", # do not build shared libs
"-DWITH_LIBSHARPYUV=OFF", # do not build libsharpyuv (for RGB <--> YUV color space conversions)
"-DWITH_AOM_DECODER=OFF", # do not build AOM V1 decoder (for AVIF image format)
"-DWITH_AOM_ENCODER=OFF", # do not build AOM V1 encoder (for AVIF image format)
"-DWITH_AOM_DECODER=ON", # enable AOM V1 decoder (for AVIF image format)
"-DWITH_AOM_ENCODER=ON", # enable AOM V1 encoder (for AVIF image format)
"-DWITH_GDK_PIXBUF=OFF", # do not build gdk-pixbuf plugin (UNIX only)
"-DWITH_GNOME=OFF", # do not build gnome plugin (Linux only)
"-DWITH_EXAMPLES=OFF", # do not build examples
@ -334,19 +378,23 @@ def make_heif(base_dir, build_type):
# add paths to dependent libraries and includes to cmake args
de265_build_dir = get_build_dir(base_dir, "libde265", platform, build_type)
x265_build_dir = get_build_dir(base_dir, "x265_git", platform, build_type)
aom_build_dir = get_build_dir(base_dir, "aom", platform, build_type)
cmake_args_ext = [
f"-DLIBDE265_INCLUDE_DIR={de265_build_dir}",
f"-DX265_INCLUDE_DIR={x265_build_dir}"
f"-DX265_INCLUDE_DIR={x265_build_dir}",
f"-DAOM_INCLUDE_DIR={base_dir}/aom"
]
if platform.find("win") != -1:
cmake_args_ext += [
f"-DLIBDE265_LIBRARY={de265_build_dir}/libde265/{build_type}/libde265.lib",
f"-DX265_LIBRARY={x265_build_dir}/{build_type}/x265-static.lib"
f"-DX265_LIBRARY={x265_build_dir}/{build_type}/x265-static.lib",
f"-DAOM_LIBRARY={aom_build_dir}/{build_type}/aom.lib"
]
else:
cmake_args_ext += [
f"-DLIBDE265_LIBRARY={de265_build_dir}/libde265/libde265.a",
f"-DX265_LIBRARY={x265_build_dir}/libx265.a"
f"-DX265_LIBRARY={x265_build_dir}/libx265.a",
f"-DAOM_LIBRARY={aom_build_dir}/aom.a"
]
# run build
build_with_cmake(platform, cmake_args + cmake_args_ext, build_type)
@ -362,6 +410,8 @@ def make_heif(base_dir, build_type):
return
def clear_module():
if base.is_dir("aom"):
base.delete_dir_with_access_error("aom")
if base.is_dir("libde265"):
base.delete_dir_with_access_error("libde265")
if base.is_dir("x265_git"):
@ -392,6 +442,8 @@ def make():
make_x265(base_dir, build_type)
# build decoder library
make_de265(base_dir, build_type)
# build decoder/encoder library for AVIF image format
make_aom(base_dir, build_type)
# build libheif
make_heif(base_dir, build_type)

View File

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

View File

@ -120,8 +120,7 @@ def make():
else:
if config.option("sysroot") != "":
base.set_sysroot_env("linux_arm64")
base.cmd("/usr/bin/perl", ["./Configure", "linux-aarch64", "enable-md2", "no-shared", "no-asm", "no-tests", "--prefix=" + old_cur_dir + "/build/linux_arm64", "--openssldir=" + old_cur_dir + "/build/linux_arm64"])
#base.cmd("./Configure", ["linux-aarch64", "enable-md2", "no-shared", "no-asm", "no-tests", "--prefix=" + old_cur_dir + "/build/linux_arm64", "--openssldir=" + old_cur_dir + "/build/linux_arm64"])
base.cmd("./Configure", ["linux-aarch64", "enable-md2", "no-shared", "no-asm", "no-tests", "--prefix=" + old_cur_dir + "/build/linux_arm64", "--openssldir=" + old_cur_dir + "/build/linux_arm64"])
base.replaceInFile("./Makefile", "CFLAGS=-Wall -O3", "CFLAGS=-Wall -O3 -fvisibility=hidden")
base.replaceInFile("./Makefile", "CXXFLAGS=-Wall -O3", "CXXFLAGS=-Wall -O3 -fvisibility=hidden")
base.cmd("make", [], True)

View File

@ -33,7 +33,7 @@ def make():
old_cur = os.getcwd()
os.chdir(base_dir)
base.common_check_version("socketio", "2", clean)
base.common_check_version("socketio", "1", clean)
os.chdir(old_cur)
if not base.is_dir(base_dir + "/socket.io-client-cpp"):

View File

@ -53,31 +53,6 @@ def change_bootstrap():
base.writeFile("./depot_tools/bootstrap/manifest.txt", content)
return
def is_ubuntu_24_or_higher():
try:
with open('/etc/os-release') as f:
for line in f:
if line.startswith('VERSION_ID='):
version = line.split('=')[1].strip().strip('"')
return float(version) >= 24
except:
pass
return False
def fix_ubuntu24():
#if not is_ubuntu_24_or_higher():
# return
if "" == config.option("sysroot"):
return
old_cur = os.getcwd()
os.chdir("third_party/llvm-build/Release+Asserts/lib")
base.cmd("mv", ["libstdc++.so.6", "libstdc++.so.6.old"])
base.cmd("ln", ["-s", "/usr/lib/x86_64-linux-gnu/libstdc++.so.6", "libstdc++.so.6"])
os.chdir(old_cur)
return
def make_args(args, platform, is_64=True, is_debug=False):
args_copy = args[:]
if is_64:
@ -91,6 +66,7 @@ def make_args(args, platform, is_64=True, is_debug=False):
args_copy = args[:]
args_copy.append("target_cpu=\\\"arm64\\\"")
args_copy.append("v8_target_cpu=\\\"arm64\\\"")
args_copy.append("use_sysroot=true")
if (platform == "win_arm64"):
args_copy = args[:]
@ -108,26 +84,8 @@ def make_args(args, platform, is_64=True, is_debug=False):
linux_clang = False
if platform == "linux":
if "" != config.option("sysroot"):
args_copy.append("use_sysroot=true")
args_copy.append("use_sysroot=false")
args_copy.append("is_clang=false")
if is_ubuntu_24_or_higher():
args_copy.append("use_gold=false")
args_copy.append("sysroot=\\\"" + config.option("sysroot_linux_64") + "\\\"")
args_copy.append("target_sysroot=\\\"" + config.option("sysroot_linux_64") + "\\\"")
else:
args_copy.append("is_clang=true")
if "1" == config.option("use-clang"):
linux_clang = True
else:
args_copy.append("use_sysroot=false")
if platform == "linux_arm64":
if "" != config.option("sysroot"):
args_copy.append("use_sysroot=true")
if is_ubuntu_24_or_higher():
args_copy.append("use_gold=false")
#args_copy.append("sysroot=\\\"" + config.option("sysroot_linux_64") + "\\\"")
args_copy.append("target_sysroot=\\\"" + config.option("sysroot_linux_arm64") + "\\\"")
else:
args_copy.append("is_clang=true")
if "1" == config.option("use-clang"):
@ -239,9 +197,6 @@ def make():
base.cmd("./depot_tools/gclient", ["sync", "-r", v8_branch_version], True)
base.cmd("gclient", ["sync", "--force"], True)
base.copy_dir("./v8/third_party_new/ninja", "./v8/third_party/ninja")
if ("linux" == base.host_platform()):
if not base.is_file("./depot_tools/python3_bin_reldir.txt"):
base.cmd_in_dir("./depot_tools", "./ensure_bootstrap", [], True)
if ("windows" == base.host_platform()):
base.replaceInFile("v8/build/config/win/BUILD.gn", ":static_crt", ":dynamic_crt")
@ -271,9 +226,6 @@ def make():
base.replaceInFile("v8/third_party/jinja2/tests.py", "from collections import Mapping", "try:\n from collections.abc import Mapping\nexcept ImportError:\n from collections import Mapping")
os.chdir("v8")
is_ubuntu24 = is_ubuntu_24_or_higher()
fix_ubuntu24()
gn_args = ["v8_static_library=true",
"is_component_build=false",
@ -285,15 +237,17 @@ def make():
if config.option("sysroot") != "":
sysroot_path = config.option("sysroot_linux_64")
sysroot_path_bin = config.get_custom_sysroot_bin("linux_64")
src_replace = "config(\"compiler\") {\n asmflags = []\n cflags = []\n cflags_c = []\n cflags_cc = []\n cflags_objc = []\n cflags_objcc = []\n ldflags = []"
dst_replace = "config(\"compiler\") {\n asmflags = []\n cflags = [\"--sysroot=" + sysroot_path + "\"]" + "\n cflags_c = []\n cflags_cc = [\"--sysroot=" + sysroot_path + "\"]" + "\n cflags_objc = []\n cflags_objcc = []\n ldflags = [\"--sysroot=" + sysroot_path + "\"]"
base.replaceInFile("build/config/compiler/BUILD.gn", src_replace, dst_replace)
src_replace = "gcc_toolchain(\"x64\") {\n cc = \"gcc\"\n cxx = \"g++\""
dst_replace = "gcc_toolchain(\"x64\") {\n cc = \""+ sysroot_path_bin + "/gcc\"\n cxx = \"" + sysroot_path_bin + "/g++\""
base.replaceInFile("build/toolchain/linux/BUILD.gn", src_replace, dst_replace)
old_env = dict(os.environ)
base.set_sysroot_env("linux_64")
pkg_old = os.environ.get("PKG_CONFIG_PATH", "")
os.environ["PKG_CONFIG_PATH"] = sysroot_path + "/usr/lib/x86_64-linux-gnu/pkgconfig:" + sysroot_path + "/usr/lib/pkgconfig:" + sysroot_path + "/usr/share/pkgconfig"
base.cmd2("gn", ["gen", "out.gn/linux_64", make_args(gn_args, "linux")], False)
os.environ["PKG_CONFIG_PATH"] = pkg_old
base.cmd2("ninja", ["-C", "out.gn/linux_64"], False)
base.restore_sysroot_env()
else:
@ -307,13 +261,7 @@ def make():
if config.check_option("platform", "linux_arm64"):
base.cmd("build/linux/sysroot_scripts/install-sysroot.py", ["--arch=arm64"], False)
sysroot_path = config.option("sysroot_linux_64")
pkg_old = os.environ.get("PKG_CONFIG_PATH", "")
os.environ["PKG_CONFIG_PATH"] = sysroot_path + "/usr/lib/x86_64-linux-gnu/pkgconfig:" + sysroot_path + "/usr/lib/pkgconfig:" + sysroot_path + "/usr/share/pkgconfig"
base.cmd2("gn", ["gen", "out.gn/linux_arm64", make_args(gn_args, "linux_arm64", False)])
os.environ["PKG_CONFIG_PATH"] = pkg_old
base.cmd("ninja", ["-C", "out.gn/linux_arm64"])
if config.check_option("platform", "mac_64"):

View File

@ -48,7 +48,6 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "HWPFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "DocxRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "StarMathConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "ooxmlsignature", "xp" if isWindowsXP else "")
base.copy_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", root_dir + "/cmap.bin")
if ("ios" == platform):

View File

@ -25,7 +25,6 @@ def make():
platform = native_platform
platform_postfix = platform + base.qt_dst_postfix()
isWindowsXP = False if (-1 == native_platform.find("_xp")) else True
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "kernel")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "kernel_network")
@ -44,7 +43,6 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "DocxRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "hunspell")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "StarMathConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, archive_dir, "ooxmlsignature", "xp" if isWindowsXP else "")
base.copy_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", archive_dir + "/cmap.bin")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, archive_dir, "x2t")

View File

@ -73,7 +73,6 @@ def make():
base.copy_lib(build_libraries_path, root_dir + "/converter", "HWPFile")
base.copy_lib(build_libraries_path, root_dir + "/converter", "DocxRenderer")
base.copy_lib(build_libraries_path, root_dir + "/converter", "StarMathConverter")
base.copy_lib(build_libraries_path, root_dir + "/converter", "ooxmlsignature", "xp" if isWindowsXP else "")
if ("ios" == platform):
base.copy_lib(build_libraries_path, root_dir + "/converter", "x2t")
@ -139,6 +138,7 @@ def make():
# libraries
base.copy_lib(build_libraries_path, root_dir, "hunspell")
base.copy_lib(build_libraries_path + ("/xp" if isWindowsXP else ""), root_dir, "ooxmlsignature")
base.copy_lib(build_libraries_path + ("/xp" if isWindowsXP else ""), root_dir, "ascdocumentscore")
if (0 != platform.find("mac")):
base.copy_lib(build_libraries_path + ("/xp" if isWindowsXP else ""), root_dir, "qtascdocumentscore")

View File

@ -66,7 +66,6 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "HWPFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "DocxRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "StarMathConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, root_dir, "ooxmlsignature")
base.copy_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", root_dir + "/cmap.bin")
if (0 == platform.find("win") or 0 == platform.find("linux") or 0 == platform.find("mac")):

View File

@ -41,7 +41,6 @@ def make():
build_server_dir = root_dir + '/server'
server_dir = base.get_script_dir() + "/../../server"
server_admin_panel_dir = base.get_script_dir() + "/../../server-admin-panel"
base.create_dir(build_server_dir + '/DocService')
@ -59,14 +58,13 @@ def make():
base.create_dir(build_server_dir + '/Metrics/node_modules/modern-syslog/build/Release')
base.copy_file(server_dir + "/Metrics/node_modules/modern-syslog/build/Release/core.node", build_server_dir + "/Metrics/node_modules/modern-syslog/build/Release/core.node")
if "server-admin-panel" in base.get_server_addons() and base.is_exist(server_admin_panel_dir):
# AdminPanel server part
base.create_dir(build_server_dir + '/AdminPanel/server')
base.copy_exe(server_admin_panel_dir + "/server", build_server_dir + '/AdminPanel/server', "adminpanel")
# AdminPanel server part
base.create_dir(build_server_dir + '/AdminPanel/server')
base.copy_exe(server_dir + "/AdminPanel/server", build_server_dir + '/AdminPanel/server', "adminpanel")
# AdminPanel client part
base.create_dir(build_server_dir + '/AdminPanel/client/build')
base.copy_dir(server_admin_panel_dir + '/client/build', build_server_dir + '/AdminPanel/client/build')
# AdminPanel client part
base.create_dir(build_server_dir + '/AdminPanel/client/build')
base.copy_dir(server_dir + '/AdminPanel/client/build', build_server_dir + '/AdminPanel/client/build')
qt_dir = base.qt_setup(native_platform)
platform = native_platform
@ -96,7 +94,6 @@ def make():
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "HWPFile")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "DocxRenderer")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "StarMathConverter")
base.copy_lib(core_build_dir + "/lib/" + platform_postfix, converter_dir, "ooxmlsignature")
base.copy_file(git_dir + "/sdkjs/pdf/src/engine/cmap.bin", converter_dir + "/cmap.bin")
base.copy_exe(core_build_dir + "/bin/" + platform_postfix, converter_dir, "x2t")
@ -183,12 +180,6 @@ def make():
base.copy_dir(document_templates_files + '/new', document_templates + '/new')
base.copy_dir(document_templates_files + '/sample', document_templates + '/sample')
#document-formats
document_formats_files = server_dir + '/../document-formats'
document_formats = build_server_dir + '/../document-formats'
base.create_dir(document_formats)
base.copy_file(document_formats_files + '/onlyoffice-docs-formats.json', document_formats + '/onlyoffice-docs-formats.json')
#license
license_file1 = server_dir + '/LICENSE.txt'
license_file2 = server_dir + '/3rd-Party.txt'

View File

@ -36,7 +36,7 @@ def s3_upload(files, dst):
def make_windows():
global package_name, package_version, arch, xp
utils.set_cwd("desktop-apps\\package")
utils.set_cwd("desktop-apps\\win-linux\\package\\windows")
package_name = branding.desktop_package_name
package_version = common.version + "." + common.build
@ -51,14 +51,13 @@ def make_windows():
if common.clean:
utils.log_h2("desktop clean")
utils.delete_dir("build")
utils.delete_files("inno\\package.config")
utils.delete_files("inno\\*.exe")
utils.delete_dir("advinst\\DesktopEditors-cache")
utils.delete_files("advinst\\package.config")
utils.delete_files("advinst\\*.msi")
utils.delete_files("advinst\\*.aic")
utils.delete_dir("zip")
utils.delete_dir("DesktopEditors-cache")
utils.delete_files("*.exe")
utils.delete_files("*.msi")
utils.delete_files("*.aic")
utils.delete_files("*.tmp")
utils.delete_files("*.zip")
utils.delete_files("data\\*.exe")
if not xp:
make_prepare()
@ -66,6 +65,8 @@ def make_windows():
if branding.onlyoffice:
make_inno()
make_inno("standalone")
if arch != "arm64":
make_inno("update")
make_advinst()
make_prepare("commercial")
@ -99,7 +100,7 @@ def make_zip(edition = "opensource"):
if edition == "commercial": zip_file = "%s-Enterprise-%s-%s.zip"
elif edition == "xp": zip_file = "%s-XP-%s-%s.zip"
else: zip_file = "%s-%s-%s.zip"
zip_file = "zip\\" + zip_file % (package_name, package_version, arch)
zip_file = zip_file % (package_name, package_version, arch)
args = [
"-Version", package_version,
"-Arch", arch,
@ -125,7 +126,7 @@ def make_inno(edition = "opensource"):
elif edition == "update": inno_file = "%s-Update-%s-%s.exe"
elif edition == "xp": inno_file = "%s-XP-%s-%s.exe"
else: inno_file = "%s-%s-%s.exe"
inno_file = "inno\\" + inno_file % (package_name, package_version, arch)
inno_file = inno_file % (package_name, package_version, arch)
args = [
"-Version", package_version,
"-Arch", arch,
@ -150,7 +151,7 @@ def make_inno(edition = "opensource"):
def make_advinst(edition = "opensource"):
if edition == "commercial": advinst_file = "%s-Enterprise-%s-%s.msi"
else: advinst_file = "%s-%s-%s.msi"
advinst_file = "advinst\\" + advinst_file % (package_name, package_version, arch)
advinst_file = advinst_file % (package_name, package_version, arch)
args = [
"-Version", package_version,
"-Arch", arch,
@ -315,7 +316,7 @@ def make_sparkle_updates():
#
def make_linux():
utils.set_cwd("desktop-apps/package")
utils.set_cwd("desktop-apps/win-linux/package/linux")
for edition in ["opensource", "commercial"]:
utils.log_h2("desktop " + edition + " build")
@ -325,7 +326,7 @@ def make_linux():
if common.platform == "linux_aarch64":
make_args += ["-e", "UNAME_M=aarch64"]
if not branding.onlyoffice:
make_args += ["-e", "BRANDING_DIR=../../" + common.branding + "/desktop-apps/package"]
make_args += ["-e", "BRANDING_DIR=../../../../" + common.branding + "/desktop-apps/win-linux/package/linux"]
ret = utils.sh("make clean && make " + " ".join(make_args), verbose=True)
utils.set_summary("desktop " + edition + " build", ret)

View File

@ -36,7 +36,7 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
return
old_env = dict(os.environ)
# qt
qt_dir = base.qt_setup(platform)
base.set_env("OS_DEPLOY", platform)
@ -50,7 +50,7 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
if (pro_dir.endswith("/")):
pro_dir = pro_dir[:-1]
makefile_name = "Makefile." + get_make_file_suffix(platform)
makefile_name = "Makefile." + get_make_file_suffix(platform)
makefile = pro_dir + "/" + makefile_name
stash_file = pro_dir + "/.qmake.stash"
@ -73,11 +73,6 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
# setup ios env
if (-1 != platform.find("ios")):
base.hack_xcode_ios()
sdk_name = "iphoneos"
if qmake_config_addon.find("ios_simulator") != -1:
sdk_name = "iphonesimulator"
base.set_env("SDK_PATH", base.find_ios_sdk(sdk_name))
base.set_env("XCODE_TOOLCHAIN_PATH", base.find_xcode_toolchain(sdk_name))
if base.is_file(makefile):
base.delete_file(makefile)
@ -105,16 +100,16 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
if "1" == config.option("use-clang"):
build_params.append("-spec")
build_params.append("linux-clang-libc++")
if "" != config.option("sysroot"):
sysroot_path = config.option("sysroot_" + platform)
os.environ['QMAKE_CUSTOM_SYSROOT'] = sysroot_path
os.environ['QMAKE_CUSTOM_SYSROOT_BIN'] = config.get_custom_sysroot_bin(platform)
os.environ['PKG_CONFIG_PATH'] = config.get_custom_sysroot_lib(platform, True) + "/pkgconfig"
os.environ['PKG_CONFIG_SYSROOT_DIR'] = sysroot_path
base.cmd_exe(qmake_app, build_params)
if "" != config.option("sysroot"):
base.set_sysroot_env(platform)
@ -122,13 +117,7 @@ def make(platform, project, qmake_config_addon="", is_no_errors=False):
if ("1" == config.option("clean")):
base.cmd_and_return_cwd("make", clean_params, True)
base.cmd_and_return_cwd("make", distclean_params, True)
if "" != config.option("sysroot"):
base.restore_sysroot_env()
base.cmd(qmake_app, build_params)
if "" != config.option("sysroot"):
base.set_sysroot_env(platform)
base.cmd(qmake_app, build_params)
base.correct_makefile_after_qmake(platform, makefile)
base.cmd_and_return_cwd("make", ["-f", makefile] + get_j_num(), is_no_errors)

View File

@ -14,7 +14,7 @@ and Plugins (Methods/Events) API using the following Python scripts:
```bash
Node.js v20 and above
Python v3.12 and above
Python v3.10 and above
```
## Installation

View File

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

View File

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

View File

@ -13,16 +13,14 @@ configs = [
"./config/word.json",
"./config/cell.json",
"./config/slide.json",
"./config/forms.json",
"./config/pdf.json"
"./config/forms.json"
]
editors_maps = {
"word": "CDE",
"cell": "CSE",
"slide": "CPE",
"forms": "CFE",
"pdf": "PDFE"
"forms": "CFE"
}
def generate(output_dir, md=False):
@ -79,7 +77,10 @@ def generate(output_dir, md=False):
doclet['example'] = remove_js_comments(comment) + "```js\n" + code_content + "\n```"
if md == False:
doclet['description'] = doclet['description'] + f'\n\n## Try it\n\n ```js document-builder={{"documentType": "{editor_name}"}}\n{code_content}\n```'
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:

View File

@ -4,16 +4,13 @@ import re
import shutil
import argparse
import generate_docs_json
import json
from pathlib import PurePosixPath
# Configuration files
editors = {
"word": "text-document-api",
"cell": "spreadsheet-api",
"slide": "presentation-api",
"forms": "form-api",
"pdf": "pdf-api",
"forms": "form-api"
}
@ -22,35 +19,8 @@ root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../.
missing_examples = []
used_enumerations = set()
translations = {}
translations_lang = None
missed_translations = {}
used_translations_keys = {}
global_output_dir = ""
cur_editor_name = None
def find_common_path_part(path_full: str, path_suffix: str, anchor: str) -> str:
path_full = path_full.replace('\\', '/')
path_suffix = path_suffix.replace('\\', '/')
parts1 = PurePosixPath(path_full).parts
parts2 = PurePosixPath(path_suffix).parts
try:
idx1 = [p.lower() for p in parts1].index(anchor.lower())
idx2 = [p.lower() for p in parts2].index(anchor.lower())
except ValueError:
return ""
common_segments = []
for p1, p2 in zip(parts1[idx1:], parts2[idx2:]):
if p1.lower() == p2.lower():
common_segments.append(p1)
else:
break
return "/".join(common_segments)
cur_editor_name = None
def load_json(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
@ -65,21 +35,6 @@ def remove_js_comments(text):
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL) # multi-line
return text.strip()
def get_translation(key):
def process_part(k):
if k not in translations:
missed_translations[k] = k
else:
used_translations_keys[k] = True
return translations.get(k, k)
if '\\\n' in key:
parts = key.split('\\\n')
translated_parts = [process_part(p) for p in parts]
return '\\\n'.join(translated_parts)
return process_part(key)
def process_link_tags(text, root=''):
"""
Finds patterns like {@link ...} and replaces them with Markdown links.
@ -87,22 +42,27 @@ def process_link_tags(text, root=''):
otherwise, a link to a class method is created.
For a method, if an alias is not specified, the name is left in the format 'Class#Method'.
"""
reserved_links = {
'/docbuilder/global#ShapeType': f"{'../../../../../../' if root == '' else '../../../../../' if root == '../' else root}docs/office-api/usage-api/text-document-api/Enumeration/ShapeType.md",
'/plugin/config': 'https://api.onlyoffice.com/docs/plugin-and-macros/structure/configuration/',
'/docbuilder/basic': 'https://api.onlyoffice.com/docs/office-api/usage-api/text-document-api/'
}
def replace_link(match):
content = match.group(1).strip() # Example: "global#ShapeType shape type" or "global#ErrorValue ErrorValue
content = match.group(1).strip() # Example: "/docbuilder/global#ShapeType shape type" or "global#ErrorValue ErrorValue"
parts = content.split()
ref = parts[0]
label = parts[1] if len(parts) > 1 else None
if ref.startswith('/docs/'):
url = root + '../../../..' + ref
display_text = label if label else ref
if url.endswith('/'):
last_dir = url.rstrip('/').split('/')[-1]
url = f"{url}{last_dir}"
return f"[{display_text}]({url}.md)"
if ref.startswith('/'):
# Handle reserved links using mapping
if ref in reserved_links:
url = reserved_links[ref]
display_text = label if label else ref
return f"[{display_text}]({url})"
else:
# If the link is not in the mapping, return the original construction
return match.group(0)
elif ref.startswith("global#"):
# Handle links to typedef (similar logic as before)
typedef_name = ref.split("#")[1]
@ -130,7 +90,7 @@ def correct_description(string, root='', isInTable=False):
- All '\r' characters are replaced with '\n'.
"""
if string is None:
return get_translation('No description provided.')
return 'No description provided.'
if False == isInTable:
# Line breaks
@ -139,7 +99,6 @@ def correct_description(string, root='', isInTable=False):
string = re.sub(r'<b>', '-**', string)
else:
string = re.sub(r'<b>', '**', string)
string = remove_line_breaks(string)
string = re.sub(r'</b>', '**', string)
@ -149,7 +108,7 @@ def correct_description(string, root='', isInTable=False):
# Process {@link ...} constructions
string = process_link_tags(string, root)
return get_translation(string)
return string
def correct_default_value(value, enumerations, classes):
if value is None or value == '':
@ -346,12 +305,12 @@ def generate_data_types_markdown(types, enumerations, classes, root='../../'):
def generate_class_markdown(class_name, methods, properties, enumerations, classes):
content = f"# {class_name}\n\n{get_translation(f"Represents the {class_name} class.")}\n\n"
content = f"# {class_name}\n\nRepresents the {class_name} class.\n\n"
content += generate_properties_markdown(properties, enumerations, classes)
content += f"\n## {get_translation(f"Methods")}\n\n"
content += f"| {get_translation(f"Method")} | {get_translation(f"Returns")} | {get_translation(f"Description")} |\n"
content += "\n## Methods\n\n"
content += "| Method | Returns | Description |\n"
content += "| ------ | ------- | ----------- |\n"
for method in sorted(methods, key=lambda m: m['name']):
@ -363,10 +322,10 @@ def generate_class_markdown(class_name, methods, properties, enumerations, class
return_type_list = returns[0].get('type', {}).get('names', [])
returns_markdown = generate_data_types_markdown(return_type_list, enumerations, classes, '../')
else:
returns_markdown = get_translation(f"None")
returns_markdown = "None"
# Processing the method description
description = correct_description(method.get('description', 'No description provided.'), '../', True)
description = remove_line_breaks(correct_description(method.get('description', 'No description provided.'), '../', True))
# Form a link to the method document
method_link = f"[{method_name}](./Methods/{method_name}.md)"
@ -388,47 +347,47 @@ def generate_method_markdown(method, enumerations, classes, example_editor_name)
# Syntax
param_list = ', '.join([param['name'] for param in params if '.' not in param['name']]) if params else ''
content += f"## {get_translation(f"Syntax")}\n\n```javascript\nexpression.{method_name}({param_list});\n```\n\n"
content += f"## Syntax\n\n```javascript\nexpression.{method_name}({param_list});\n```\n\n"
if memberof:
content += f"`expression` - {get_translation(f"A variable that represents a [{memberof}](../{memberof}.md) class.")}\n\n"
content += f"`expression` - A variable that represents a [{memberof}](../{memberof}.md) class.\n\n"
# Parameters
content += f"## {get_translation(f"Parameters")}\n\n"
content += "## Parameters\n\n"
if params:
content += f"| **{get_translation(f"Name")}** | **{get_translation(f"Required/Optional")}** | **{get_translation(f"Data type")}** | **{get_translation(f"Default")}** | **{get_translation(f"Description")}** |\n"
content += "| **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 = correct_description(param.get('description', 'No description provided.'), '../../', True)
param_required = f"{get_translation(f"Required")}" if not param.get('optional') else f"{get_translation(f"Optional")}"
param_desc = remove_line_breaks(correct_description(param.get('description', 'No description provided.'), '../../', True))
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 += f"{get_translation("This method doesn't have any parameters.")}\n"
content += "This method doesn't have any parameters.\n"
# Returns
content += f"\n## {get_translation(f"Returns")}\n\n"
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 += get_translation(f"This method doesn't return any data.")
content += "This method doesn't return any data."
# Example
if example:
# Separate comment and code, remove JS comments
if '```js' in example:
comment, code = example.split('```js', 1)
comment = get_translation(comment.strip())
content += f"\n\n## {get_translation(f"Example")}\n\n{comment}\n\n```javascript {example_editor_name}\n{code.strip()}\n"
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## {get_translation(f"Example")}\n\n```javascript {example_editor_name}\n{cleaned_example}\n```\n"
content += f"\n\n## Example\n\n```javascript {example_editor_name}\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
@ -436,14 +395,14 @@ def generate_properties_markdown(properties, enumerations, classes, root='../'):
if properties is None:
return ''
content = f"## {get_translation(f"Properties")}\n\n"
content += f"| {get_translation(f"Name")} | {get_translation(f"Type")} | {get_translation(f"Description")} |\n"
content = "## Properties\n\n"
content += "| Name | Type | Description |\n"
content += "| ---- | ---- | ----------- |\n"
for prop in sorted(properties, key=lambda m: m['name']):
prop_name = prop['name']
prop_description = prop.get('description', 'No description provided.')
prop_description = correct_description(prop_description, root, True)
prop_description = remove_line_breaks(correct_description(prop_description, root, True))
prop_types = prop['type']['names'] if prop.get('type') else []
param_types_md = generate_data_types_markdown(prop_types, enumerations, classes, root)
content += f"| {prop_name} | {param_types_md} | {prop_description} |\n"
@ -467,8 +426,8 @@ def generate_enumeration_markdown(enumeration, enumerations, classes, example_ed
if ptype['type'] == 'TypeUnion':
enum_empty = True # is empty enum
content += f"## {get_translation(f"Type")}\n\n{get_translation(f"Enumeration")}\n\n"
content += f"## {get_translation(f"Values")}\n\n"
content += "## Type\n\nEnumeration\n\n"
content += "## Values\n\n"
# Each top-level name in the union
for raw_t in enumeration['type']['names']:
ts_t = convert_jsdoc_array_to_ts(raw_t)
@ -488,25 +447,25 @@ def generate_enumeration_markdown(enumeration, enumerations, classes, example_ed
if enum_empty == True:
return None
elif enumeration['properties'] is not None:
content += f"## {get_translation(f"Type")}\n\n{get_translation(f"Object")}\n\n"
content += "## Type\n\nObject\n\n"
content += generate_properties_markdown(enumeration['properties'], enumerations, classes)
else:
content += f"## {get_translation(f"Type")}\n\n"
content += "## Type\n\n"
# If it's not a union and has no properties, simply print the type(s).
types = enumeration['type']['names']
t_md = generate_data_types_markdown(types, enumerations, classes, '../')
t_md = generate_data_types_markdown(types, enumerations, classes)
content += t_md + "\n\n"
# Example
if example:
if '```js' in example:
comment, code = example.split('```js', 1)
comment = get_translation(comment.strip())
content += f"\n\n## {get_translation(f"Example")}\n\n{comment}\n\n```javascript {example_editor_name}\n{code.strip()}\n"
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## {get_translation(f"Example")}\n\n```javascript {example_editor_name}\n{cleaned_example}\n```\n"
content += f"\n\n## Example\n\n```javascript {example_editor_name}\n{cleaned_example}\n```\n"
return escape_text_outside_code_blocks(content)
@ -523,13 +482,11 @@ def process_doclets(data, output_dir, editor_name):
if editor_name == 'word':
example_editor_name += 'docx'
elif editor_name == 'forms':
example_editor_name += 'forms'
example_editor_name += 'pdf'
elif editor_name == 'slide':
example_editor_name += 'pptx'
elif editor_name == 'cell':
example_editor_name += 'xlsx'
elif editor_name == 'pdf':
example_editor_name += 'pdf'
for doclet in data:
if doclet['kind'] == 'class':
@ -596,18 +553,7 @@ def process_doclets(data, output_dir, editor_name):
if not enum.get('example', ''):
missing_examples.append(os.path.relpath(enum_file_path, output_dir))
def generate(output_dir, translations_file):
global translations
global translations_lang
global global_output_dir
global_output_dir = output_dir
if translations_file is not None and os.path.exists(translations_file):
translations = load_json(translations_file)
translations_lang = os.path.splitext(os.path.basename(translations_file))[0]
else:
translations = {}
def generate(output_dir):
os.chdir(os.path.dirname(script_path))
print('Generating Markdown documentation...')
@ -626,21 +572,6 @@ def generate(output_dir, translations_file):
used_enumerations.clear()
process_doclets(data, output_dir, editor_name)
if translations_file is not None:
target_dir = os.path.dirname(translations_file)
missed_file_path = os.path.join(target_dir, "missed_translations.json")
print(f'Saving missed translations to: {missed_file_path}')
with open(missed_file_path, 'w', encoding='utf-8') as f:
json.dump(missed_translations, f, ensure_ascii=False, indent=4)
unused_keys = set(translations.keys()) - set(used_translations_keys.keys())
unused_data = {k: translations[k] for k in unused_keys}
unused_file_path = os.path.join(target_dir, "unused_translations.json")
print(f'Saving unused translations to: {unused_file_path}')
with open(unused_file_path, 'w', encoding='utf-8') as f:
json.dump(unused_data, f, ensure_ascii=False, indent=4)
shutil.rmtree(output_dir + 'tmp_json')
print('Done')
@ -653,17 +584,8 @@ if __name__ == "__main__":
nargs='?', # Indicates the argument is optional
default=f"{root}/api.onlyoffice.com/site/docs/office-api/usage-api/" # Default value
)
parser.add_argument(
"--translations",
type=str,
help="Path to the JSON file with translations",
nargs='?',
default=None
)
args = parser.parse_args()
generate(args.destination, args.translations)
generate(args.destination)
print("START_MISSING_EXAMPLES")
print(",".join(missing_examples))
print("END_MISSING_EXAMPLES")

View File

@ -11,16 +11,14 @@ editors = [
"word",
"cell",
"slide",
"forms",
"pdf"
"forms"
]
editors_names = {
"word": "Word",
"cell": "Spreadsheet",
"slide": "Presentation",
"forms": "Forms",
"pdf": "PDF"
"forms": "Forms"
}
script_path = os.path.abspath(__file__)

View File

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

View File

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

View File

@ -10,8 +10,7 @@ configs = [
"./config/events/word.json",
"./config/events/cell.json",
"./config/events/slide.json",
"./config/events/forms.json",
"./config/events/pdf.json"
"./config/events/forms.json"
]
script_path = os.path.abspath(__file__)

View File

@ -5,16 +5,13 @@ import re
import shutil
import argparse
import generate_docs_events_json
import json
from pathlib import PurePosixPath
# Папки для каждого editor_name
editors = {
"word": "text-document-api",
"cell": "spreadsheet-api",
"slide": "presentation-api",
"forms": "form-api",
"pdf": "pdf-api"
"forms": "form-api"
}
script_path = os.path.abspath(__file__)
@ -22,34 +19,7 @@ root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../.
missing_examples = []
used_enumerations = set()
translations = {}
translations_lang = None
missed_translations = {}
used_translations_keys = {}
global_output_dir = ""
def find_common_path_part(path_full: str, path_suffix: str, anchor: str) -> str:
path_full = path_full.replace('\\', '/')
path_suffix = path_suffix.replace('\\', '/')
parts1 = PurePosixPath(path_full).parts
parts2 = PurePosixPath(path_suffix).parts
try:
idx1 = [p.lower() for p in parts1].index(anchor.lower())
idx2 = [p.lower() for p in parts2].index(anchor.lower())
except ValueError:
return ""
common_segments = []
for p1, p2 in zip(parts1[idx1:], parts2[idx2:]):
if p1.lower() == p2.lower():
common_segments.append(p1)
else:
break
return "/".join(common_segments)
def load_json(path):
with open(path, 'r', encoding='utf-8') as f:
@ -67,20 +37,6 @@ def remove_js_comments(text):
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL)
return text.strip()
def get_translation(key):
def process_part(k):
if k not in translations:
missed_translations[k] = k
else:
used_translations_keys[k] = True
return translations.get(k, k)
if '\\\n' in key:
parts = key.split('\\\n')
translated_parts = [process_part(p) for p in parts]
return '\\\n'.join(translated_parts)
return process_part(key)
def correct_description(string, root='', isInTable=False):
"""
@ -92,7 +48,7 @@ def correct_description(string, root='', isInTable=False):
- All '\r' characters are replaced with '\n'.
"""
if string is None:
return get_translation('No description provided.')
return 'No description provided.'
if False == isInTable:
# Line breaks
@ -101,7 +57,6 @@ def correct_description(string, root='', isInTable=False):
string = re.sub(r'<b>', '-**', string)
else:
string = re.sub(r'<b>', '**', string)
string = remove_line_breaks(string)
string = re.sub(r'</b>', '**', string)
@ -111,7 +66,7 @@ def correct_description(string, root='', isInTable=False):
# Process {@link ...} constructions
string = process_link_tags(string, root)
return get_translation(string)
return string
def process_link_tags(text, root=''):
"""
@ -120,22 +75,31 @@ def process_link_tags(text, root=''):
otherwise, a link to a class method is created.
For a method, if an alias is not specified, the name is left in the format 'Class#Method'.
"""
reserved_links = {
'/docbuilder/global#ShapeType': f"{'../../../../../../' if root == '' else '../../../../../' if root == '../' else root}docs/office-api/usage-api/text-document-api/Enumeration/ShapeType.md",
'/plugin/config': 'https://api.onlyoffice.com/docs/plugin-and-macros/structure/configuration/',
'/docbuilder/basic': 'https://api.onlyoffice.com/docs/office-api/usage-api/text-document-api/'
}
def replace_link(match):
content = match.group(1).strip() # Example: "global#ShapeType shape type" or "global#ErrorValue ErrorValue
content = match.group(1).strip() # Example: "/docbuilder/global#ShapeType shape type" or "global#ErrorValue ErrorValue"
parts = content.split()
ref = parts[0]
label = parts[1] if len(parts) > 1 else None
if ref.startswith('/docs/'):
url = root + '../../../..' + ref
display_text = label if label else ref
if url.endswith('/'):
last_dir = url.rstrip('/').split('/')[-1]
url = f"{url}{last_dir}"
return f"[{display_text}]({url}.md)"
if ref.startswith('/'):
# Handle reserved links using mapping
if ref in reserved_links:
url = reserved_links[ref]
display_text = label if label else ref
return f"[{display_text}]({url})"
elif ref.startswith('/docs/plugins/'):
url = f"../../{ref.split('/docs/plugins/')[1]}.md"
display_text = label if label else ref
return f"[{display_text}]({url})"
else:
# If the link is not in the mapping, return the original construction
return match.group(0)
elif ref.startswith("global#"):
# Handle links to typedef (similar logic as before)
typedef_name = ref.split("#")[1]
@ -196,26 +160,26 @@ def escape_text_outside_code_blocks(md):
def generate_event_markdown(event, enumerations):
name = event['name']
desc = correct_description(event.get('description', ''), '../', True)
desc = correct_description(event.get('description', ''))
params = event.get('params', [])
md = f"# {name}\n\n{desc}\n\n"
# Parameters
md += f"## {get_translation("Parameters")}\n\n"
md += "## Parameters\n\n"
if params:
md += f"| **{get_translation("Name")}** | **{get_translation("Data type")}** | **{get_translation("Description")}** |\n"
md += "| **Name** | **Data type** | **Description** |\n"
md += "| --------- | ------------- | ----------- |\n"
for p in params:
t_md = generate_data_types_markdown(
p.get('type', {}).get('names', []),
enumerations
)
d = correct_description(p.get('description', ''), isInTable=True)
d = remove_line_breaks(correct_description(p.get('description', ''), isInTable=True))
md += f"| {p['name']} | {t_md} | {d} |\n"
md += "\n"
else:
md += f"{get_translation("This event has no parameters.")}\n\n"
md += "This event has no parameters.\n\n"
for ex in event.get('examples', []):
code = remove_js_comments(ex).strip()
@ -248,7 +212,7 @@ def generate_enumeration_markdown(enumeration, enumerations):
# If parsedType is missing, just list 'type.names' if available
type_names = enumeration['type'].get('names', [])
if type_names:
content += f"## {get_translation("Type")}\n\n"
content += "## Type\n\n"
t_md = generate_data_types_markdown(type_names, enumerations)
content += t_md + "\n\n"
else:
@ -256,8 +220,8 @@ def generate_enumeration_markdown(enumeration, enumerations):
# 1) Handle TypeUnion
if ptype == 'TypeUnion':
content += f"## {get_translation("Type")}\n\n{get_translation("Enumeration")}\n\n"
content += f"## {get_translation("Values")}\n\n"
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):
@ -268,11 +232,11 @@ def generate_enumeration_markdown(enumeration, enumerations):
# 2) Handle TypeApplication (e.g. Object.<string, string>)
elif ptype == 'TypeApplication':
content += f"## {get_translation("Type")}\n\n{get_translation("Object")}\n\n"
content += "## Type\n\nObject\n\n"
type_names = enumeration['type'].get('names', [])
if type_names:
t_md = generate_data_types_markdown(type_names, enumerations)
content += f"**{get_translation("Type")}:** {t_md}\n\n"
content += f"**Type:** {t_md}\n\n"
# 3) If properties are present, treat it like an object
if enumeration.get('properties') is not None:
@ -282,16 +246,16 @@ def generate_enumeration_markdown(enumeration, enumerations):
if ptype not in ('TypeUnion', 'TypeApplication'):
type_names = enumeration['type'].get('names', [])
if type_names:
content += f"## {get_translation("Type")}\n\n"
content += "## Type\n\n"
t_md = generate_data_types_markdown(type_names, enumerations)
content += t_md + "\n\n"
# Process examples array
if examples:
if len(examples) > 1:
content += f"\n\n## {get_translation("Examples")}\n\n"
content += "\n\n## Examples\n\n"
else:
content += f"\n\n## {get_translation("Example")}\n\n"
content += "\n\n## Example\n\n"
for i, ex_line in enumerate(examples, start=1):
# Remove JS comments
@ -300,15 +264,15 @@ def generate_enumeration_markdown(enumeration, enumerations):
# Attempt splitting if the user used ```js
if '```js' in cleaned_example:
comment, code = cleaned_example.split('```js', 1)
comment = get_translation(comment.strip())
comment = comment.strip()
code = code.strip()
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"**Example {i}:**\n\n{comment}\n\n"
content += f"```javascript\n{code}\n```\n"
else:
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"**Example {i}:**\n\n{comment}\n\n"
# No special fences, just show as code
content += f"```javascript\n{cleaned_example}\n```\n"
@ -319,13 +283,13 @@ def generate_events_summary(events):
Create Events.md summary listing all events with their description.
"""
header = [
f"# {get_translation("Events")}\n\n",
f"| {get_translation("Event")} | {get_translation("Description")} |\n",
"# Events\n\n",
"| Event | Description |\n",
"| ----- | ----------- |\n"
]
lines = [
f"| [{ev['name']}](./{ev['name']}.md) | "
f"{correct_description(ev.get('description', ''), '../', isInTable=True)} |\n"
f"{remove_line_breaks(correct_description(ev.get('description', ''), isInTable=True))} |\n"
for ev in sorted(events, key=lambda e: e['name'])
]
return "".join(header + lines)
@ -334,14 +298,14 @@ def generate_properties_markdown(properties, enumerations):
if properties is None:
return ''
content = f"## {get_translation("Properties")}\n\n"
content += f"| {get_translation("Name")} | {get_translation("Type")} | {get_translation("Description")} |\n"
content = "## Properties\n\n"
content += "| Name | Type | Description |\n"
content += "| ---- | ---- | ----------- |\n"
for prop in sorted(properties, key=lambda m: m['name']):
prop_name = prop['name']
prop_description = prop.get('description', 'No description provided.')
prop_description = correct_description(prop_description, isInTable=True)
prop_description = remove_line_breaks(correct_description(prop_description, isInTable=True))
prop_types = prop['type']['names'] if prop.get('type') else []
param_types_md = generate_data_types_markdown(prop_types, enumerations)
content += f"| {prop_name} | {param_types_md} | {prop_description} |\n"
@ -410,18 +374,7 @@ def process_events(data, editor_dir):
# events summary
write_markdown_file(os.path.join(events_dir, "Events.md"), generate_events_summary(events))
def generate_events(output_dir, translations_file):
global translations
global translations_lang
global global_output_dir
global_output_dir = output_dir
if translations_file is not None and os.path.exists(translations_file):
translations = load_json(translations_file)
translations_lang = os.path.splitext(os.path.basename(translations_file))[0]
else:
translations = {}
def generate_events(output_dir):
os.chdir(os.path.dirname(script_path))
if output_dir.endswith('/'):
@ -434,21 +387,6 @@ def generate_events(output_dir, translations_file):
data = load_json(os.path.join(tmp, f"{editor_name}.json"))
process_events(data, os.path.join(output_dir, folder))
if translations_file is not None:
target_dir = os.path.dirname(translations_file)
missed_file_path = os.path.join(target_dir, "missed_translations.json")
print(f'Saving missed translations to: {missed_file_path}')
with open(missed_file_path, 'w', encoding='utf-8') as f:
json.dump(missed_translations, f, ensure_ascii=False, indent=4)
unused_keys = set(translations.keys()) - set(used_translations_keys.keys())
unused_data = {k: translations[k] for k in unused_keys}
unused_file_path = os.path.join(target_dir, "unused_translations.json")
print(f'Saving unused translations to: {unused_file_path}')
with open(unused_file_path, 'w', encoding='utf-8') as f:
json.dump(unused_data, f, ensure_ascii=False, indent=4)
shutil.rmtree(tmp)
print("Done. Missing examples:", missing_examples)
@ -461,14 +399,5 @@ if __name__ == "__main__":
default=f"{root}/api.onlyoffice.com/site/docs/plugin-and-macros/interacting-with-editors/",
help="Output directory"
)
parser.add_argument(
"--translations",
type=str,
help="Path to the JSON file with translations",
nargs='?',
default=None
)
args = parser.parse_args()
generate_events(args.destination, args.translations)
generate_events(args.destination)

View File

@ -10,8 +10,7 @@ configs = [
"./config/methods/word.json",
"./config/methods/cell.json",
"./config/methods/slide.json",
"./config/methods/forms.json",
"./config/methods/pdf.json"
"./config/methods/forms.json"
]
script_path = os.path.abspath(__file__)

View File

@ -4,16 +4,13 @@ import re
import shutil
import argparse
import generate_docs_methods_json
import json
from pathlib import PurePosixPath
# Configuration files
editors = {
"word": "text-document-api",
"cell": "spreadsheet-api",
"slide": "presentation-api",
"forms": "form-api",
"pdf": "pdf-api"
"forms": "form-api"
}
script_path = os.path.abspath(__file__)
@ -21,35 +18,8 @@ root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../.
missing_examples = []
used_enumerations = set()
translations = {}
translations_lang = None
missed_translations = {}
used_translations_keys = {}
global_output_dir = ""
cur_editor_name = None
def find_common_path_part(path_full: str, path_suffix: str, anchor: str) -> str:
path_full = path_full.replace('\\', '/')
path_suffix = path_suffix.replace('\\', '/')
parts1 = PurePosixPath(path_full).parts
parts2 = PurePosixPath(path_suffix).parts
try:
idx1 = [p.lower() for p in parts1].index(anchor.lower())
idx2 = [p.lower() for p in parts2].index(anchor.lower())
except ValueError:
return ""
common_segments = []
for p1, p2 in zip(parts1[idx1:], parts2[idx2:]):
if p1.lower() == p2.lower():
common_segments.append(p1)
else:
break
return "/".join(common_segments)
cur_editor_name = None
def load_json(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
@ -64,21 +34,6 @@ def remove_js_comments(text):
text = re.sub(r'/\*.*?\*/', '', text, flags=re.DOTALL) # multi-line
return text.strip()
def get_translation(key):
def process_part(k):
if k not in translations:
missed_translations[k] = k
else:
used_translations_keys[k] = True
return translations.get(k, k)
if '\\\n' in key:
parts = key.split('\\\n')
translated_parts = [process_part(p) for p in parts]
return '\\\n'.join(translated_parts)
return process_part(key)
def process_link_tags(text, root=''):
"""
Finds patterns like {@link ...} and replaces them with Markdown links.
@ -86,28 +41,36 @@ def process_link_tags(text, root=''):
otherwise, a link to a class method is created.
For a method, if an alias is not specified, the name is left in the format 'Class#Method'.
"""
reserved_links = {
'/docbuilder/global#ShapeType': f"{'../../../../../' if root == '' else '../../../../' if root == '../' else root}docs/office-api/usage-api/text-document-api/Enumeration/ShapeType.md",
'/plugin/config': 'https://api.onlyoffice.com/docs/plugin-and-macros/structure/configuration/',
'/docbuilder/basic': 'https://api.onlyoffice.com/docs/office-api/usage-api/text-document-api/'
}
def replace_link(match):
content = match.group(1).strip() # Example: "global#ShapeType shape type" or "global#ErrorValue ErrorValue
content = match.group(1).strip() # Example: "/docbuilder/global#ShapeType shape type" or "global#ErrorValue ErrorValue"
parts = content.split()
ref = parts[0]
label = parts[1] if len(parts) > 1 else None
if ref.startswith('/docs/'):
url = root + '../../../..' + ref
display_text = label if label else ref
if url.endswith('/'):
last_dir = url.rstrip('/').split('/')[-1]
url = f"{url}{last_dir}"
return f"[{display_text}]({url}.md)"
if ref.startswith('/'):
# Handle reserved links using mapping
if ref in reserved_links:
url = reserved_links[ref]
display_text = label if label else ref
return f"[{display_text}]({url})"
else:
# If the link is not in the mapping, return the original construction
return match.group(0)
elif ref.startswith("global#"):
# Handle links to typedef (similar logic as before)
typedef_name = ref.split("#")[1]
used_enumerations.add(typedef_name)
display_text = label if label else typedef_name
return f"[{display_text}]({root}Enumeration/{typedef_name}.md)"
elif ref.startswith("https"):
display_text = label if label else ref # Keep the full notation, e.g., "Api#CreateSlide"
return f"[{display_text}]({ref})"
else:
# Handle links to class methods like ClassName#MethodName
try:
@ -129,7 +92,7 @@ def correct_description(string, root='', isInTable=False):
- All '\r' characters are replaced with '\n'.
"""
if string is None:
return get_translation('No description provided.')
return 'No description provided.'
if False == isInTable:
# Line breaks
@ -138,7 +101,6 @@ def correct_description(string, root='', isInTable=False):
string = re.sub(r'<b>', '-**', string)
else:
string = re.sub(r'<b>', '**', string)
string = remove_line_breaks(string)
string = re.sub(r'</b>', '**', string)
@ -148,7 +110,7 @@ def correct_description(string, root='', isInTable=False):
# Process {@link ...} constructions
string = process_link_tags(string, root)
return get_translation(string)
return string
def correct_default_value(value, enumerations, classes):
if value is None or value == '':
@ -346,12 +308,12 @@ def generate_data_types_markdown(types, enumerations, classes, root='../'):
return param_types_md
def generate_class_markdown(class_name, methods, properties, enumerations, classes):
content = f"# {class_name}\n\n{get_translation(f"Represents the {class_name} class.")}\n\n"
content = f"# {class_name}\n\nRepresents the {class_name} class.\n\n"
content += generate_properties_markdown(properties, enumerations, classes)
content += f"\n## {get_translation("Methods")}\n\n"
content += f"| {get_translation("Method")} | {get_translation("Returns")} | {get_translation("Description")} |\n"
content += "\n## Methods\n\n"
content += "| Method | Returns | Description |\n"
content += "| ------ | ------- | ----------- |\n"
for method in sorted(methods, key=lambda m: m['name']):
@ -363,10 +325,10 @@ def generate_class_markdown(class_name, methods, properties, enumerations, class
return_type_list = returns[0].get('type', {}).get('names', [])
returns_markdown = generate_data_types_markdown(return_type_list, enumerations, classes, '../')
else:
returns_markdown = get_translation("None")
returns_markdown = "None"
# Processing the method description
description = correct_description(method.get('description', 'No description provided.'), '../', True)
description = remove_line_breaks(correct_description(method.get('description', 'No description provided.'), '../', True))
# Form a link to the method document
method_link = f"[{method_name}](./{method_name}.md)"
@ -395,42 +357,42 @@ def generate_method_markdown(method, enumerations, classes):
# Syntax
param_list = ', '.join([param['name'] for param in params if '.' not in param['name']]) if params else ''
content += f"## {get_translation("Syntax")}\n\n```javascript\nexpression.{method_name}({param_list});\n```\n\n"
content += f"## Syntax\n\n```javascript\nexpression.{method_name}({param_list});\n```\n\n"
if memberof:
content += f"`expression` - {get_translation(f"A variable that represents a [{memberof}](Methods.md) class.")}\n\n"
content += f"`expression` - A variable that represents a [{memberof}](Methods.md) class.\n\n"
# Parameters
content += f"## {get_translation("Parameters")}\n\n"
content += "## Parameters\n\n"
if params:
content += f"| **{get_translation("Name")}** | **{get_translation("Required/Optional")}** | **{get_translation("Data type")}** | **{get_translation("Default")}** | **{get_translation("Description")}** |\n"
content += "| **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 = correct_description(param.get('description', 'No description provided.'), '../', True)
param_required = get_translation("Required") if not param.get('optional') else get_translation("Optional")
param_desc = remove_line_breaks(correct_description(param.get('description', 'No description provided.'), '../', True))
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 += f"{get_translation("This method doesn't have any parameters.")}\n"
content += "This method doesn't have any parameters.\n"
# Returns
content += f"\n## {get_translation("Returns")}\n\n"
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 += get_translation("This method doesn't return any data.")
content += "This method doesn't return any data."
# Process examples array
if examples:
if len(examples) > 1:
content += f"\n\n## {get_translation("Examples")}\n\n"
content += "\n\n## Examples\n\n"
else:
content += f"\n\n## {get_translation("Example")}\n\n"
content += "\n\n## Example\n\n"
for i, ex_line in enumerate(examples, start=1):
# Remove JS comments
@ -439,15 +401,15 @@ def generate_method_markdown(method, enumerations, classes):
# Attempt splitting if the user used ```js
if '```js' in cleaned_example:
comment, code = cleaned_example.split('```js', 1)
comment = get_translation(comment.strip())
comment = comment.strip()
code = code.strip()
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"**Example {i}:**\n\n{comment}\n\n"
content += f"```javascript\n{code}\n```\n"
else:
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"**Example {i}:**\n\n{comment}\n\n"
# No special fences, just show as code
content += f"```javascript\n{cleaned_example}\n```\n"
@ -457,14 +419,14 @@ def generate_properties_markdown(properties, enumerations, classes, root='../'):
if properties is None:
return ''
content = f"## {get_translation("Properties")}\n\n"
content += f"| {get_translation("Name")} | {get_translation("Type")} | {get_translation("Description")} |\n"
content = "## Properties\n\n"
content += "| Name | Type | Description |\n"
content += "| ---- | ---- | ----------- |\n"
for prop in sorted(properties, key=lambda m: m['name']):
prop_name = prop['name']
prop_description = prop.get('description', 'No description provided.')
prop_description = correct_description(prop_description, root, isInTable=True)
prop_description = remove_line_breaks(correct_description(prop_description, isInTable=True))
prop_types = prop['type']['names'] if prop.get('type') else []
param_types_md = generate_data_types_markdown(prop_types, enumerations, classes, root)
content += f"| {prop_name} | {param_types_md} | {prop_description} |\n"
@ -496,7 +458,7 @@ def generate_enumeration_markdown(enumeration, enumerations, classes):
# If parsedType is missing, just list 'type.names' if available
type_names = enumeration['type'].get('names', [])
if type_names:
content += f"## {get_translation("Type")}\n\n"
content += "## Type\n\n"
t_md = generate_data_types_markdown(type_names, enumerations, classes)
content += t_md + "\n\n"
else:
@ -504,8 +466,8 @@ def generate_enumeration_markdown(enumeration, enumerations, classes):
# 1) Handle TypeUnion
if ptype == 'TypeUnion':
content += f"## {get_translation("Type")}\n\n{get_translation("Enumeration")}\n\n"
content += f"## {get_translation("Values")}\n\n"
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):
@ -518,11 +480,11 @@ def generate_enumeration_markdown(enumeration, enumerations, classes):
# 2) Handle TypeApplication (e.g. Object.<string, string>)
elif ptype == 'TypeApplication':
content += f"## {get_translation("Type")}\n\n{get_translation("Object")}\n\n"
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"**{get_translation("Type")}:** {t_md}\n\n"
content += f"**Type:** {t_md}\n\n"
# 3) If properties are present, treat it like an object
if enumeration.get('properties') is not None:
@ -532,16 +494,16 @@ def generate_enumeration_markdown(enumeration, enumerations, classes):
if ptype not in ('TypeUnion', 'TypeApplication'):
type_names = enumeration['type'].get('names', [])
if type_names:
content += f"## {get_translation("Type")}\n\n"
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 += f"\n\n## {get_translation("Examples")}\n\n"
content += "\n\n## Examples\n\n"
else:
content += f"\n\n## {get_translation("Example")}\n\n"
content += "\n\n## Example\n\n"
for i, ex_line in enumerate(examples, start=1):
# Remove JS comments
@ -550,15 +512,15 @@ def generate_enumeration_markdown(enumeration, enumerations, classes):
# Attempt splitting if the user used ```js
if '```js' in cleaned_example:
comment, code = cleaned_example.split('```js', 1)
comment = get_translation(comment.strip())
comment = comment.strip()
code = code.strip()
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"**Example {i}:**\n\n{comment}\n\n"
content += f"```javascript\n{code}\n```\n"
else:
if len(examples) > 1:
content += f"**{get_translation("Example")} {i}:**\n\n{comment}\n\n"
content += f"**Example {i}:**\n\n{comment}\n\n"
# No special fences, just show as code
content += f"```javascript\n{cleaned_example}\n```\n"
@ -654,18 +616,7 @@ def process_doclets(data, output_dir, editor_name):
if not enum.get('examples', ''):
missing_examples.append(os.path.relpath(enum_file_path, output_dir))
def generate(output_dir, translations_file):
global translations
global translations_lang
global global_output_dir
global_output_dir = output_dir
if translations_file is not None and os.path.exists(translations_file):
translations = load_json(translations_file)
translations_lang = os.path.splitext(os.path.basename(translations_file))[0]
else:
translations = {}
def generate(output_dir):
os.chdir(os.path.dirname(script_path))
print('Generating Markdown documentation...')
@ -681,21 +632,6 @@ def generate(output_dir, translations_file):
used_enumerations.clear()
process_doclets(data, output_dir, editor_name)
if translations_file is not None:
target_dir = os.path.dirname(translations_file)
missed_file_path = os.path.join(target_dir, "missed_translations.json")
print(f'Saving missed translations to: {missed_file_path}')
with open(missed_file_path, 'w', encoding='utf-8') as f:
json.dump(missed_translations, f, ensure_ascii=False, indent=4)
unused_keys = set(translations.keys()) - set(used_translations_keys.keys())
unused_data = {k: translations[k] for k in unused_keys}
unused_file_path = os.path.join(target_dir, "unused_translations.json")
print(f'Saving unused translations to: {unused_file_path}')
with open(unused_file_path, 'w', encoding='utf-8') as f:
json.dump(unused_data, f, ensure_ascii=False, indent=4)
shutil.rmtree(output_dir + '/tmp_json')
print('Done')
@ -708,17 +644,8 @@ if __name__ == "__main__":
nargs='?', # Indicates the argument is optional
default=f"{root}/api.onlyoffice.com/site/docs/plugin-and-macros/interacting-with-editors/" # Default value
)
parser.add_argument(
"--translations",
type=str,
help="Path to the JSON file with translations",
nargs='?',
default=None
)
args = parser.parse_args()
generate(args.destination, args.translations)
generate(args.destination)
print("START_MISSING_EXAMPLES")
print(",".join(missing_examples))
print("END_MISSING_EXAMPLES")

View File

@ -33,8 +33,6 @@
"core/DesktopEditor/doctrenderer/doctrenderer.pro",
"core/DesktopEditor/xmlsec/src/ooxmlsignature.pro",
"[!no_x2t]core/OOXML/Projects/Linux/DocxFormatLib/DocxFormatLib.pro",
"[!no_x2t]core/OOXML/Projects/Linux/PPTXFormatLib/PPTXFormatLib.pro",
"[!no_x2t]core/OOXML/Projects/Linux/XlsbFormatLib/XlsbFormatLib.pro",
@ -88,6 +86,8 @@
"core",
"multimedia",
"core/DesktopEditor/xmlsec/src/ooxmlsignature.pro",
"desktop-sdk/ChromiumBasedEditors/lib/ascdocumentscore.pro",
"desktop-sdk/ChromiumBasedEditors/lib/ascdocumentscore_helper.pro",

View File

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

View File

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

View File

@ -16,7 +16,6 @@ def install_deps():
"build-essential",
"ca-certificates",
"cmake",
"perl",
"curl",
"git",
"glib-2.0-dev",

View File

@ -146,10 +146,8 @@ def fix_absolute_symlinks(sysroot_path, arch):
if arch == "arm64":
lib_dir = os.path.join(sysroot_path, "usr/lib/aarch64-linux-gnu")
target_lib_dir = "aarch64-linux-gnu"
elif arch == "amd64":
lib_dir = os.path.join(sysroot_path, "usr/lib/x86_64-linux-gnu")
target_lib_dir = "x86_64-linux-gnu"
else:
return
@ -184,14 +182,6 @@ def fix_absolute_symlinks(sysroot_path, arch):
print(f" Fixed: {filename} -> {relative_target}")
fixed_count += 1
libgcc_usr_symlink = os.path.join(lib_dir, "libgcc_s.so")
libgcc_target = os.path.join(sysroot_path, "lib", target_lib_dir, "libgcc_s.so.1")
if os.path.exists(libgcc_target) and not os.path.exists(libgcc_usr_symlink):
absolute_libgcc = os.path.abspath(libgcc_target)
os.symlink(absolute_libgcc, libgcc_usr_symlink)
fixed_count += 1
print(f"Fixed {fixed_count} absolute symlinks")

View File

@ -1 +1 @@
9.3.1
9.2.1