diff --git a/configure.py b/configure.py index 5b31229..34b64b6 100755 --- a/configure.py +++ b/configure.py @@ -43,6 +43,7 @@ parser.add_option("--vs-path", action="store", type="string", dest="vs-path", de parser.add_option("--siteUrl", action="store", type="string", dest="siteUrl", default="127.0.0.1", help="site url") parser.add_option("--multiprocess", action="store", type="string", dest="multiprocess", default="1", help="provides ability to specify single process for make") parser.add_option("--sysroot", action="store", type="string", dest="sysroot", default="0", help="provides ability to use sysroot (ubuntu 16.04) to build c++ code. If value is \"1\", then the sysroot from tools/linux/sysroot will be used, and if it is not there, it will download it and unpack it. You can also set value as the path to the your own sysroot (rarely used). Only for linux") +parser.add_option("--qemu-win-arm64-dir", action="store", type="string", dest="qemu-win-arm64-dir", default="", help="dir to qemu virtual machine for win_arm64 cross build. It should contains start.bat. More info in tools/win/qemu.") (options, args) = parser.parse_args(arguments) configOptions = vars(options) diff --git a/scripts/base.py b/scripts/base.py index b1f6cf5..a8b71fc 100644 --- a/scripts/base.py +++ b/scripts/base.py @@ -1972,13 +1972,43 @@ def get_autobuild_version(product, platform="", branch="", build=""): download_addon = download_branch + "/" + download_build + "/" + product + "-" + download_platform + ".7z" return "http://repo-doc-onlyoffice-com.s3.amazonaws.com/archive/" + download_addon +def is_use_create_artifacts_qemu_any_platform(): + if config.check_option("platform", "win_arm64") and not is_os_arm(): + return True + return False + +def is_use_create_artifacts_qemu(platform): + if platform == "win_arm64" and not is_os_arm(): + return True + return False + +def create_artifacts_qemu_any_platform(): + if not is_use_create_artifacts_qemu_any_platform(): + return + if config.check_option("platform", "win_arm64"): + create_artifacts_qemu_win_arm() + return; + +def create_artifacts_qemu_win_arm(): + if config.option("qemu-win-arm64-dir") == "": + print("For deploying win_arm64 on non arm host you should provide qemu-win-arm64-dir. More info in tools/win/qemu/README.md") + return + + old_curr_dir = os.path.abspath(os.curdir) + qemu_dir = os.path.abspath(config.option("qemu-win-arm64-dir")) + + os.chdir(qemu_dir) + start_qemu_bat_path = f"start.bat" + cmd(start_qemu_bat_path, []) + os.chdir(old_curr_dir) + def create_x2t_js_cache(dir, product, platform): # mac if is_file(dir + "/libdoctrenderer.dylib") or is_dir(dir + "/doctrenderer.framework"): doctrenderer_lib = "libdoctrenderer.dylib" if is_file(dir + "/libdoctrenderer.dylib") else "doctrenderer.framework/doctrenderer" if os.path.getsize(dir + "/" + doctrenderer_lib) < 5*1024*1024: return - + if ((platform == "linux_arm64") and not is_os_arm()): cmd_in_dir_qemu(platform, dir, "./x2t", ["-create-js-snapshots"], True) return diff --git a/scripts/core_common/modules/socket_io.py b/scripts/core_common/modules/socket_io.py index 8c86c2e..02d10b0 100644 --- a/scripts/core_common/modules/socket_io.py +++ b/scripts/core_common/modules/socket_io.py @@ -8,6 +8,11 @@ import os import subprocess import glob +def clean(): + if base.is_dir("socket.io-client-cpp"): + base.delete_dir_with_access_error("socket.io-client-cpp") + return + def correct_namespace(dir): folder = dir if ("/" != folder[-1:]): @@ -25,6 +30,12 @@ def correct_namespace(dir): def make(): base_dir = base.get_script_dir() + "/../../core/Common/3dParty/socketio" + + old_cur = os.getcwd() + os.chdir(base_dir) + base.common_check_version("socketio", "1", clean) + os.chdir(old_cur) + if not base.is_dir(base_dir + "/socket.io-client-cpp"): base.cmd_in_dir(base_dir, "git", ["clone", "https://github.com/socketio/socket.io-client-cpp.git"]) base.cmd_in_dir(base_dir + "/socket.io-client-cpp", "git", ["checkout", "da779141a7379cc30c870d48295033bc16a23c66"]) @@ -37,6 +48,7 @@ def make(): base.apply_patch(base_dir + "/socket.io-client-cpp/src/internal/sio_client_impl.cpp", base_dir + "/patches/sio_client_impl_fail.patch") base.apply_patch(base_dir + "/socket.io-client-cpp/src/internal/sio_client_impl.cpp", base_dir + "/patches/sio_client_impl_open.patch") base.apply_patch(base_dir + "/socket.io-client-cpp/src/internal/sio_client_impl.cpp", base_dir + "/patches/sio_client_impl_close_timeout.patch") + base.apply_patch(base_dir + "/socket.io-client-cpp/src/internal/sio_client_impl.cpp", base_dir + "/patches/sio_client_impl_encode.patch") # no tls realization (remove if socket.io fix this) dst_dir = base_dir + "/socket.io-client-cpp/src_no_tls" diff --git a/scripts/core_common/modules/v8_89.py b/scripts/core_common/modules/v8_89.py index 9d642aa..fd524e9 100644 --- a/scripts/core_common/modules/v8_89.py +++ b/scripts/core_common/modules/v8_89.py @@ -16,7 +16,11 @@ def change_bootstrap(): content += "infra/3pp/tools/cpython/${platform} version:2@2.7.18.chromium.39\n\n" content += "@Subdir python3\n" - content += "infra/3pp/tools/cpython3/${platform} version:2@3.8.10.chromium.23\n\n" + + if ("windows" == base.host_platform()): + content += "infra/3pp/tools/cpython3/${platform} version:2@3.11.8.chromium.35\n\n" + else: + content += "infra/3pp/tools/cpython3/${platform} version:2@3.8.10.chromium.23\n\n" content += "@Subdir git\n" content += "infra/3pp/tools/git/${platform} version:2@2.41.0.chromium.11\n" @@ -74,6 +78,9 @@ def make_args(args, platform, is_64=True, is_debug=False): if (platform == "windows"): args_copy.append("is_clang=false") + if (platform == "mac") and base.is_os_arm(): + args_copy.append("host_cpu=\\\"x64\\\"") + if linux_clang != True: args_copy.append("use_custom_libcxx=false") diff --git a/scripts/deploy.py b/scripts/deploy.py index 2c53dde..517b539 100644 --- a/scripts/deploy.py +++ b/scripts/deploy.py @@ -22,4 +22,6 @@ def make(): deploy_mobile.make() if config.check_option("module", "osign"): deploy_osign.make() + if base.is_use_create_artifacts_qemu_any_platform(): + base.create_artifacts_qemu_any_platform() return diff --git a/scripts/deploy_desktop.py b/scripts/deploy_desktop.py index a5d6ec5..ceefdcb 100644 --- a/scripts/deploy_desktop.py +++ b/scripts/deploy_desktop.py @@ -288,12 +288,12 @@ def make(): is_host_not_arm = False host_platform = "" - if (platform == "mac_arm64" or platform == "win_arm64") and not base.is_os_arm(): + + # TODO: fix this on mac_arm64 (qemu) + # on windows we are using qemu + if (platform == "mac_arm64") and not base.is_os_arm(): is_host_not_arm = True - if platform == "mac_arm64": - host_platform = "mac_64" - elif platform == "win_arm64": - host_platform = "win_64" + host_platform = "mac_64" # all themes generate ---- base.copy_exe(core_build_dir + "/bin/" + platform_postfix, root_dir + "/converter", "allfontsgen") @@ -306,10 +306,11 @@ def make(): if is_host_not_arm: sdkjs_dir = root_dir + "/editors/sdkjs" - end_find_platform = sdkjs_dir.rfind("/" + platform + "/") - sdkjs_dir_64 = sdkjs_dir[0:end_find_platform] + "/" + host_platform + "/" + sdkjs_dir[end_find_platform+11:] + str1 = "/" + platform + "/" + str2 = "/" + host_platform + "/" + sdkjs_dir_host = sdkjs_dir.replace(str1, str2) base.delete_dir(sdkjs_dir) - base.copy_dir(sdkjs_dir_64, sdkjs_dir) + base.copy_dir(sdkjs_dir_host, sdkjs_dir) else: themes_params = [] if ("" != config.option("themesparams")): @@ -320,10 +321,11 @@ def make(): base.delete_file(root_dir + "/converter/font_selection.bin") base.delete_file(root_dir + "/converter/fonts.log") - base.delete_exe(root_dir + "/converter/allfontsgen") - base.delete_exe(root_dir + "/converter/allthemesgen") + if not base.is_use_create_artifacts_qemu(platform): + base.delete_exe(root_dir + "/converter/allfontsgen") + base.delete_exe(root_dir + "/converter/allthemesgen") if not isUseJSC: base.delete_file(root_dir + "/editors/sdkjs/slide/sdk-all.cache") - return + return \ No newline at end of file diff --git a/scripts/sdkjs_common/jsdoc/office-api/generate_docs_json.py b/scripts/sdkjs_common/jsdoc/office-api/generate_docs_json.py index b175a44..b9cc080 100644 --- a/scripts/sdkjs_common/jsdoc/office-api/generate_docs_json.py +++ b/scripts/sdkjs_common/jsdoc/office-api/generate_docs_json.py @@ -5,7 +5,8 @@ import argparse import re import platform -root = '../../../../..' +script_path = os.path.abspath(__file__) +root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..')) # Configuration files configs = [ @@ -23,6 +24,8 @@ editors_maps = { } def generate(output_dir, md=False): + os.chdir(os.path.dirname(script_path)) + if not os.path.exists(output_dir): os.makedirs(output_dir) diff --git a/scripts/sdkjs_common/jsdoc/office-api/generate_docs_md.py b/scripts/sdkjs_common/jsdoc/office-api/generate_docs_md.py index cafc8d2..6c5fc36 100644 --- a/scripts/sdkjs_common/jsdoc/office-api/generate_docs_md.py +++ b/scripts/sdkjs_common/jsdoc/office-api/generate_docs_md.py @@ -13,6 +13,10 @@ editors = { "forms": "form-api" } + +script_path = os.path.abspath(__file__) +root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..')) + missing_examples = [] used_enumerations = set() @@ -550,6 +554,8 @@ def process_doclets(data, output_dir, editor_name): missing_examples.append(os.path.relpath(enum_file_path, output_dir)) def generate(output_dir): + os.chdir(os.path.dirname(script_path)) + print('Generating Markdown documentation...') generate_docs_json.generate(output_dir + 'tmp_json', md=True) @@ -576,7 +582,7 @@ if __name__ == "__main__": type=str, help="Destination directory for the generated documentation", nargs='?', # Indicates the argument is optional - default="../../../../../api.onlyoffice.com/site/docs/office-api/usage-api/" # Default value + default=f"{root}/api.onlyoffice.com/site/docs/office-api/usage-api/" # Default value ) args = parser.parse_args() generate(args.destination) diff --git a/scripts/sdkjs_common/jsdoc/office-api/generate_jsonl_dataset.py b/scripts/sdkjs_common/jsdoc/office-api/generate_jsonl_dataset.py index 5187d0d..ccad596 100644 --- a/scripts/sdkjs_common/jsdoc/office-api/generate_jsonl_dataset.py +++ b/scripts/sdkjs_common/jsdoc/office-api/generate_jsonl_dataset.py @@ -21,7 +21,9 @@ editors_names = { "forms": "Forms" } -root = '../../../../..' +script_path = os.path.abspath(__file__) +root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..')) + missing_examples = [] def load_json(file_path): @@ -199,6 +201,8 @@ def process_doclets(doclets, output_entries, editor_name, model): output_entries.append(create_entry(system_message, comment, assistant_message, model)) def generate(output_dir, model): + os.chdir(os.path.dirname(script_path)) + print('Generating documentation JSONL dataset...') shutil.rmtree(output_dir, ignore_errors=True) @@ -228,7 +232,7 @@ if __name__ == "__main__": type=str, help="Destination directory for the generated documentation", nargs='?', # Indicates the argument is optional - default="../../../../../office-js-api/dataset" # Default value + default=f"{root}/office-js-api/dataset" # Default value ) parser.add_argument( "model", diff --git a/scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_json.py b/scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_json.py index 850aa14..7e6702f 100644 --- a/scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_json.py +++ b/scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_json.py @@ -13,9 +13,12 @@ configs = [ "./config/events/forms.json" ] -root = '../../../../..' +script_path = os.path.abspath(__file__) +root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..')) def generate(output_dir, md=False): + os.chdir(os.path.dirname(script_path)) + if not os.path.exists(output_dir): os.makedirs(output_dir) diff --git a/scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_md.py b/scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_md.py index 12e0299..4140cea 100644 --- a/scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_md.py +++ b/scripts/sdkjs_common/jsdoc/plugins/generate_docs_events_md.py @@ -14,6 +14,9 @@ editors = { "forms": "form-api" } +script_path = os.path.abspath(__file__) +root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..')) + missing_examples = [] used_enumerations = set() @@ -372,6 +375,8 @@ def process_events(data, editor_dir): write_markdown_file(os.path.join(events_dir, "Events.md"), generate_events_summary(events)) def generate_events(output_dir): + os.chdir(os.path.dirname(script_path)) + if output_dir.endswith('/'): output_dir = output_dir[:-1] tmp = os.path.join(output_dir, 'tmp_json') @@ -391,7 +396,7 @@ if __name__ == "__main__": parser.add_argument( "destination", nargs="?", - default="../../../../../api.onlyoffice.com/site/docs/plugin-and-macros/interacting-with-editors/", + default=f"{root}/api.onlyoffice.com/site/docs/plugin-and-macros/interacting-with-editors/", help="Output directory" ) args = parser.parse_args() diff --git a/scripts/sdkjs_common/jsdoc/plugins/generate_docs_methods_json.py b/scripts/sdkjs_common/jsdoc/plugins/generate_docs_methods_json.py index 7f55b3c..44ac0bb 100644 --- a/scripts/sdkjs_common/jsdoc/plugins/generate_docs_methods_json.py +++ b/scripts/sdkjs_common/jsdoc/plugins/generate_docs_methods_json.py @@ -13,9 +13,12 @@ configs = [ "./config/methods/forms.json" ] -root = '../../../../..' +script_path = os.path.abspath(__file__) +root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..')) def generate(output_dir, md=False): + os.chdir(os.path.dirname(script_path)) + if not os.path.exists(output_dir): os.makedirs(output_dir) diff --git a/scripts/sdkjs_common/jsdoc/plugins/generate_docs_methods_md.py b/scripts/sdkjs_common/jsdoc/plugins/generate_docs_methods_md.py index 65ab6c2..14635cc 100644 --- a/scripts/sdkjs_common/jsdoc/plugins/generate_docs_methods_md.py +++ b/scripts/sdkjs_common/jsdoc/plugins/generate_docs_methods_md.py @@ -13,6 +13,9 @@ editors = { "forms": "form-api" } +script_path = os.path.abspath(__file__) +root = os.path.abspath(os.path.join(os.path.dirname(script_path), '../../../../..')) + missing_examples = [] used_enumerations = set() @@ -614,6 +617,8 @@ def process_doclets(data, output_dir, editor_name): missing_examples.append(os.path.relpath(enum_file_path, output_dir)) def generate(output_dir): + os.chdir(os.path.dirname(script_path)) + print('Generating Markdown documentation...') if output_dir[-1] == '/': @@ -637,7 +642,7 @@ if __name__ == "__main__": type=str, help="Destination directory for the generated documentation", nargs='?', # Indicates the argument is optional - default="../../../../../api.onlyoffice.com/site/docs/plugin-and-macros/interacting-with-editors/" # Default value + default=f"{root}/api.onlyoffice.com/site/docs/plugin-and-macros/interacting-with-editors/" # Default value ) args = parser.parse_args() generate(args.destination) diff --git a/tools/win/qemu/.gitignore b/tools/win/qemu/.gitignore new file mode 100644 index 0000000..86538f9 --- /dev/null +++ b/tools/win/qemu/.gitignore @@ -0,0 +1,5 @@ +* +!.gitignore +!README.md +!automate.bat +!start.bat \ No newline at end of file diff --git a/tools/win/qemu/README.md b/tools/win/qemu/README.md new file mode 100644 index 0000000..a930b75 --- /dev/null +++ b/tools/win/qemu/README.md @@ -0,0 +1,102 @@ +# **Setting up QEMU Windows 11** + +## **0. Preparing for Installation** + +1) Install [**MSYS2**](https://www.msys2.org/). The following commands assume that the installation directory remains the default. +2) ```pacman -S mingw-w64-x86_64-qemu``` +3) Verify installation: ```C:\msys64\mingw64\bin\qemu-system-aarch64.exe --version``` +4) Create an image: ```C:\msys64\mingw64\bin\qemu-img create win11-arm64.img 40G``` +5) Download the Windows 11 ARM64 ISO (a VPN may be required): https://www.microsoft.com/en-us/software-download/windows11arm64 +6) Download the drivers: https://github.com/virtio-win/virtio-win-pkg-scripts/blob/master/README.md +7) Download the bootloader: https://packages.debian.org/sid/qemu-efi-aarch64 + +```ar x qemu-efi-aarch64*.deb``` + +```tar xvf data.tar.xz``` + +```mv ./usr/share/qemu-efi-aarch64/QEMU_EFI.fd .``` + +8) Run _start.bat_ + +**Note:** If you plan to install the VM elsewhere, copy _start.bat_ to that same location. + +--- + +## **1. Installing and Configuring QEMU** + +1. To install QEMU and run a Windows ARM64 virtual machine, follow this tutorial: + [Windows ARM64 VM using qemu-system](https://linaro.atlassian.net/wiki/spaces/WOAR/pages/28914909194/windows-arm64+VM+using+qemu-system). + During installation, there may be no “Back” button (as was my case); if so, you need to set the required registry variables beforehand. + It is strongly recommended to select the **Pro** version — functionality is not guaranteed on the Home edition. +2. Installing connection drivers is essential; details are provided at the end of the tutorial. Other drivers are optional. + +--- + +## **2. Creating a Shared Folder** + +To share files between the host Windows and the guest OS, create a folder, for example **D:\shared**. +The _shared_ folder can also be a working repository folder; in that case, replace _shared_ with your folder name later on. + +1. Right-click the folder → **Properties**. +2. Go to **Sharing** → **Share**. +3. In the list, select **Everyone** (or a specific user), click **Add**, grant **Read/Write** permissions, and then click **Share**. + +You may also need to allow guest connections both on the host and in the guest OS. Instructions are available [here](https://learn.microsoft.com/en-us/windows-server/storage/file-server/enable-insecure-guest-logons-smb2-and-smb3?tabs=powershell). +Now the folder is accessible from the guest machine. + +--- + +## **3. Verification** + +After starting the virtual machine, open **Command Prompt** or **PowerShell** **without** administrator rights and run: + +``` +net use Z: \\10.0.2.2\shared /user:guest "" +dir Z: +``` + +These commands should complete without errors and display a list of directories and files in the shared folder. + +--- + +## **4. Installing VC_redist** + +Additional packages are required for running software. You can download them inside the guest machine, but that may take a long time, so it’s recommended to download them on the host, place them in the shared folder, and install them from there. + +Download from: +https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170 (choose ARM64 version). + +To install from the command line after connecting the shared folder: + +``` +.\Z:\VC_redist.arm64.exe +``` + +--- + +## **5. Setting Up Automatic Script Execution via Task Scheduler** + +Before creating a task, remove the password from your user account so that the system logs in automatically when the VM starts. + +### **Creating the Task** + +1. Open **Task Scheduler** → **Create Task...** +2. **General** tab: + - Name: Automate + - Run only when user is logged on + - Run with highest privileges +3. **Triggers** tab: + - New → **At log on** +4. **Actions** tab: + - Action: **Start a program** + - Program/script: + `\\10.0.2.2\shared\build_tools\tools\win\qemu\automate.bat` + +--- + +## **6. Config Setup** + +Finally, specify the path to the folder containing the VM and the _start.bat_ script in the configuration under +```qemu-win-arm64-dir```. + +You can do this either through `configure.py` or by editing the configuration file directly. diff --git a/tools/win/qemu/automate.bat b/tools/win/qemu/automate.bat new file mode 100644 index 0000000..168ec6b --- /dev/null +++ b/tools/win/qemu/automate.bat @@ -0,0 +1,52 @@ +setlocal enabledelayedexpansion + +set "script_dir=%~dp0" +set "config_file=%script_dir%..\..\..\config" +set "git_dir=%script_dir%..\..\..\..\ + +set "module_list=" +set "branding_value=" +set "themesparams=" + +for /f "delims=" %%a in ('type "%config_file%"') do ( + set "line=%%a" + if "!line:~0,7!"=="module=" ( + set "module_list=!line:~7!" + set "module_list=!module_list:"=!" + ) + if "!line:~0,14!"=="branding-name=" ( + set "branding_value=!line:~14!" + set "branding_value=!branding_value:"=!" + ) + if "!line:~0,13!"=="themesparams=" ( + set "themesparams=!line:~13!" + set "themesparams=!branding_value:"=!" + ) +) + +if "!branding_value!"=="" ( + set "branding_value=onlyoffice" +) + +set "base_out_dir=%script_dir%..\..\..\out\win_arm64\%branding_value%" + +for %%m in (!module_list!) do ( + if "%%m"=="desktop" ( + call %base_out_dir%\DesktopEditors\converter\x2t.exe -create-js-snapshots + call %base_out_dir%\DesktopEditors\converter\allfontsgen.exe --use-system="1" --input="%base_out_dir%\DesktopEditors\fonts" --input="%git_dir%\core-fonts" --allfonts="%base_out_dir%\DesktopEditors\converter\AllFonts.js" --selection="%base_out_dir%\DesktopEditors\converter\font_selection.bin" + call %base_out_dir%\DesktopEditors\converter\allthemesgen.exe --converter-dir="%base_out_dir%\DesktopEditors\converter" --src="%base_out_dir%\DesktopEditors\editors\sdkjs\slide\themes" --allfonts="AllFonts.js" --output="%base_out_dir%\DesktopEditors\editors\sdkjs\common\Images" --params="%themesparams%" + del %base_out_dir%\DesktopEditors\converter\allfontsgen.exe + del %base_out_dir%\DesktopEditors\converter\allthemesgen.exe + + ) + if "%%m"=="server" ( + call %base_out_dir%\documentserver\server\FileConverter\bin\x2t -create-js-snapshots + ) + if "%%m"=="builder" ( + call %base_out_dir%\DocumentBuilder\x2t -create-js-snapshots + ) + if "%%m"=="core" ( + call %base_out_dir%\core\x2t -create-js-snapshots + ) +) +shutdown /s /f /t 10 \ No newline at end of file diff --git a/tools/win/qemu/start.bat b/tools/win/qemu/start.bat new file mode 100644 index 0000000..665a474 --- /dev/null +++ b/tools/win/qemu/start.bat @@ -0,0 +1,17 @@ +@echo off +REM ======================================================== +REM INSTALL Windows ARM64 VM (QEMU on Windows x64) +REM ======================================================== + +C:\msys64\mingw64\bin\qemu-system-aarch64 ^ + -M virt -m 8G -cpu max,pauth-impdef=on -smp 8 ^ + -bios ./QEMU_EFI.fd ^ + -accel tcg,thread=multi ^ + -device ramfb ^ + -device qemu-xhci -device usb-kbd -device usb-tablet ^ + -nic user,model=virtio-net-pci ^ + -device usb-storage,drive=install ^ + -drive if=none,id=install,format=raw,media=cdrom,file=./Win11_24H2_English_Arm64.iso ^ + -device usb-storage,drive=virtio-drivers ^ + -drive if=none,id=virtio-drivers,format=raw,media=cdrom,file=./virtio-win-0.1.271.iso ^ + -drive if=virtio,id=system,format=raw,file=./win11-arm64.img