前几个月买了个华为的平板,一直想趁机搞搞鸿蒙开发,但苦于 DevEco 不支持 Linux,遂开始考虑是否可能进行移植。
准备工作#
获取 DevEco Studio 的 Windows版#
我们这里从华为官方下载Windows版本的安装包(本文撰写时可下载的版本是DevEco Studio 6.0.2 Release ↗),通过wine安装即可。安装配置全部保持默认,则DevEco的程序本体应该在~/.wine/drive_c/Program Files/Huawei/DevEco Studio下,我们进入该目录。
获取 IDEA#
由于目前的 DevEco Studio 是基于IDEA修改制作而成的,所以我们需要一份IDEA的Linux版作为参考,我下载的版本是2025.3.2 ↗。
进行解压和整理后我们的目录格式如下:
.
├── DevEco Studio
│ ├── bin
│ ├── ......
│ └── tools
└── idea-IU-253.30387.90
├── bin
├── ......
└── pluginsbash移植环境#
移植JBR#
JBR ( Jetbrains Runtime ) 是由 JetBrains 制作的 JDK 改版,我们下载 IDEA 时就已经包含在内。直接复制替换DevEco Studio中的jbr目录即可。
当然,IDEA 和 DevEco 使用的 JBR 版本不一定相同,如果你希望 JBR 版本完全匹配,可以通过wine打印一下DevEco的jbr版本,再到Github ↗上进行下载。
移植Node#
华为的构建工具要么是用nodejs写的要么使用python写的,查看tools/node,发现NodeJS版本是v18.20.1 LTS ↗,于是在官网下载该版本,解压,替换原目录。(这里为了和Windows版本的目录结构保持一致,我将node/bin子目录下的文件全部软链接到了node目录)
此时tools目录下应该有node文件夹。
替换libs#
进入lib目录,复制 IDEA 样本中的native, pty4j, jna三个目录并替换DevEco中的目录
替换OpenHarmony SDK#
这里用下载好的OpenHarmony SDK替换sdk/default/openharmony即可。(至于如何下载,可以直接使用DevEco提供的下载工具,你可以先跳过本步,做完其他步骤,之后应该能在Linux上打开它并下载SDK了)
逆向官方启动器#
由于华子大概率是直接搬的官方启动器,这里我直接用 ida 逆了 bin/idea。最有用的函数应该是xplat_launcheer::main_lib。在GPT的帮助下得出的 结论如下:
- 启动器使用
世界上最好的语言Rust 编写 - 启动器加载时会向当前目录的上层递归读取
product-info.json文件,最大深度为5 - 启动器会加载properties文件,具体加载的是
因此,我们通过编写product-info.json和devecostudio.vmoption,即可完成最后的移植工作。
编写相关配置文件#
这部分就不多赘述,给出可以抄作业的VMOption & product-info & idea properties:
// product-info.json
{
"name": "DevEco Studio",
"version": "6.0.2.640",
"versionSuffix": "",
"buildNumber": "243.24978.46.36.602640",
"productCode": "DS",
"envVarBaseName": "DEVECOSTUDIO",
"dataDirectoryName": "DevEcoStudio6.0",
"svgIconPath": "bin/devecostudio.svg",
"productVendor": "Huawei",
"launch": [
{
"os": "Linux",
"arch": "amd64",
"launcherPath": "bin/devecostudio",
"javaExecutablePath": "jbr/bin/java",
"vmOptionsFilePath": "bin/devecostudio64-lin.vmoptions",
"startupWmClass": "deveco-studio",
"bootClassPathJarNames": [
"platform-loader.jar",
"util-8.jar",
"util.jar",
"util_rt.jar",
"opentelemetry.jar",
"app.jar",
"stats.jar",
"jps-model.jar",
"external-system-rt.jar",
"rd.jar",
"bouncy-castle.jar",
"protobuf.jar",
"forms_rt.jar",
"lib.jar",
"externalProcess-rt.jar",
"groovy.jar",
"annotations.jar",
"hwlib.jar",
"idea_rt.jar",
"kotlinx-coroutines-slf4j-1.8.0-intellij.jar",
"nio-fs.jar",
"trove.jar"
],
"additionalJvmArguments": [
"-Djava.system.class.loader=com.intellij.util.lang.PathClassLoader",
"-Didea.vendor.name=Huawei",
"-Didea.paths.selector=DevEcoStudio6.0",
"-Djna.boot.library.path=$IDE_HOME/lib/jna/amd64",
"-Dpty4j.preferred.native.folder=$IDE_HOME/lib/pty4j",
"-Djna.nosys=true",
"-Djna.noclasspath=true",
"-Dintellij.platform.runtime.repository.path=$IDE_HOME/modules/module-descriptors.jar",
"-Didea.platform.prefix=DevEcoStudio",
"-Dsplash=true",
"-Daether.connector.resumeDownloads=false",
"-Dcompose.swing.render.on.graphics=true",
"--add-opens=java.base/java.io=ALL-UNNAMED",
"--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.lang.ref=ALL-UNNAMED",
"--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",
"--add-opens=java.base/java.net=ALL-UNNAMED",
"--add-opens=java.base/java.nio=ALL-UNNAMED",
"--add-opens=java.base/java.nio.charset=ALL-UNNAMED",
"--add-opens=java.base/java.text=ALL-UNNAMED",
"--add-opens=java.base/java.time=ALL-UNNAMED",
"--add-opens=java.base/java.util=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED",
"--add-opens=java.base/jdk.internal.vm=ALL-UNNAMED",
"--add-opens=java.base/sun.net.dns=ALL-UNNAMED",
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED",
"--add-opens=java.base/sun.nio.fs=ALL-UNNAMED",
"--add-opens=java.base/sun.security.ssl=ALL-UNNAMED",
"--add-opens=java.base/sun.security.util=ALL-UNNAMED",
"--add-opens=java.desktop/com.sun.java.swing=ALL-UNNAMED",
"--add-opens=java.desktop/com.sun.java.swing.plaf.gtk=ALL-UNNAMED",
"--add-opens=java.desktop/java.awt=ALL-UNNAMED",
"--add-opens=java.desktop/java.awt.dnd.peer=ALL-UNNAMED",
"--add-opens=java.desktop/java.awt.event=ALL-UNNAMED",
"--add-opens=java.desktop/java.awt.font=ALL-UNNAMED",
"--add-opens=java.desktop/java.awt.image=ALL-UNNAMED",
"--add-opens=java.desktop/java.awt.peer=ALL-UNNAMED",
"--add-opens=java.desktop/javax.swing=ALL-UNNAMED",
"--add-opens=java.desktop/javax.swing.plaf.basic=ALL-UNNAMED",
"--add-opens=java.desktop/javax.swing.text=ALL-UNNAMED",
"--add-opens=java.desktop/javax.swing.text.html=ALL-UNNAMED",
"--add-opens=java.desktop/javax.swing.text.html.parser=ALL-UNNAMED",
"--add-opens=java.desktop/sun.awt=ALL-UNNAMED",
"--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED",
"--add-opens=java.desktop/sun.awt.datatransfer=ALL-UNNAMED",
"--add-opens=java.desktop/sun.awt.image=ALL-UNNAMED",
"--add-opens=java.desktop/sun.font=ALL-UNNAMED",
"--add-opens=java.desktop/sun.java2d=ALL-UNNAMED",
"--add-opens=java.desktop/sun.swing=ALL-UNNAMED",
"--add-opens=java.management/sun.management=ALL-UNNAMED",
"--add-opens=jdk.attach/sun.tools.attach=ALL-UNNAMED",
"--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
"--add-opens=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED",
"--add-opens=jdk.jdi/com.sun.tools.jdi=ALL-UNNAMED"
],
"mainClass": "com.intellij.idea.Main"
}
],
"bundledPlugins": [],
"modules": [],
"fileExtensions": [],
"layout": []
}json# devecostudio64-lin.vmoptions
-Xms256m
-Xmx2048m
-XX:ReservedCodeCacheSize=512m
-XX:CICompilerCount=2
-XX:+HeapDumpOnOutOfMemoryError
-XX:-OmitStackTraceInFastThrow
-XX:+IgnoreUnrecognizedVMOptions
-XX:+UnlockDiagnosticVMOptions
-XX:TieredOldPercentage=100000
-ea
-Dfile.encoding=UTF-8
-Dsun.java2d.metal=true
-Dawt.lock.fair=true
-Dsun.io.useCanonCaches=false
-Djdk.nio.maxCachedBufferSize=2097152
-Djava.util.zip.use.nio.for.zip.file.access=true
-Djava.nio.file.spi.DefaultFileSystemProvider=com.intellij.platform.core.nio.fs.MultiRoutingFileSystemProvider
-Djdk.attach.allowAttachSelf=true
-Dsun.tools.attach.tmp.only=true
-Djdk.http.auth.tunneling.disabledSchemes=""
-Djdk.module.illegalAccess.silent=true
-Djbr.catch.SIGABRT=true
-Dkotlinx.coroutines.debug=off
-Djava.security.manager=com.intellij.platform.core.nio.fs.CoreBootstrapSecurityManagerbash# idea.properties
# ---------------------------------------------------------------------
# DevEco Studio - Linux
# Based on IntelliJ Platform 243.x
# ---------------------------------------------------------------------
# Use ${idea.home.path} macro to specify location relative to IDE installation home.
# Use ${xxx} where xxx is any Java property (including defined in previous lines of this file)
# to refer to its value.
# ---------------------------------------------------------------------
# Paths (use defaults unless you really need to override)
# ---------------------------------------------------------------------
# idea.config.path=${user.home}/.DevEcoStudio/config
# idea.system.path=${user.home}/.DevEcoStudio/system
# idea.plugins.path=${idea.config.path}/plugins
# idea.log.path=${idea.system.path}/log
# ---------------------------------------------------------------------
# File size limits
# ---------------------------------------------------------------------
idea.max.intellisense.filesize=2500
idea.max.content.load.filesize=20000
# ---------------------------------------------------------------------
# Console & process behavior
# ---------------------------------------------------------------------
idea.cycle.buffer.size=4096
idea.no.launcher=false
idea.dynamic.classpath=false
# ---------------------------------------------------------------------
# UI / Window manager behavior
# ---------------------------------------------------------------------
idea.popup.weight=heavy
# ---------------------------------------------------------------------
# Graphics & rendering (Linux-safe)
# ---------------------------------------------------------------------
sun.java2d.d3d=false
sun.java2d.pmoffscreen=false
swing.bufferPerWindow=true
sun.java2d.uiScale.enabled=false
javax.swing.rebaseCssSizeMap=true
# ---------------------------------------------------------------------
# Accessibility & stability (JBR)
# ---------------------------------------------------------------------
javax.swing.JTree.excludeAccessibleChildrenFromClosedNodes=true
sun.awt.mac.a11y.tableAccessibleRowCountThreshold=1000
sun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree=true
# ---------------------------------------------------------------------
# Smooth scrolling (disabled by default, stable choice)
# ---------------------------------------------------------------------
# idea.true.smooth.scrolling=true
# ---------------------------------------------------------------------
# DevEco specific: plugin compatibility baseline
# IMPORTANT: keeps DevEco plugins from behaving like pure IDEA plugins
# ---------------------------------------------------------------------
idea.plugins.compatible.build=IC-243.24978.46
# ---------------------------------------------------------------------
# HarmonyOS / DevEco toolchain (keep even if commented)
# ---------------------------------------------------------------------
# ide.harmonyos.sdk.location=/path/hosdk/
# ide.ohpm.location=/path/ohpm
# ide.hvigor.location=/path/hvigor
# ide.node.location=/path/node
# ide.emulator.location=/path/emulator
# ---------------------------------------------------------------------
# ArkTS / Node related limits (optional)
# ---------------------------------------------------------------------
# arkts.server.max.intellisense.filesize=10240
# arkts.server.max.old.space.size=8192
# ---------------------------------------------------------------------
# Error handling
# ---------------------------------------------------------------------
idea.fatal.error.notification=disabledproperties这里值得注意的一个 fun fact 是:如果你使用sun.java2d.uiScale=true开启 IDEA 推荐的 Scale by OS,华为奇怪的插件加载机制会搞砸一切!所以这里设置为false,我们就完成了全部的移植工作。
处理签名问题#
此时的DevEco看起来已经可以正常开发了,但在签名时会遇到奇怪的问题:
2026-02-05 22:20:54,000 [ 490226] WARN - #com.huawei.deveco.projectmgmt.hos.s
ignature.sign.utils.AutoGenerateKeyCSRUtil - No available JDK is found.
2026-02-05 22:21:00,716 [ 496942] WARN - #com.huawei.deveco.projectmgmt.hos.s
ignature.sign.utils.AutoGenerateKeyCSRUtil - No available JDK is found.plaintext我们使用 Recaf 逆向该组件。(如果有做Java逆向的朋友,真心推荐Recaf,目前用过的最好用的Java反编译/字节码修改工具,绝对的神器级别)。组件位置是plugins/harmony/lib/hos-project-mgmt-6.0.2.640.jar,相关代码是:
String toolPath = homePath + TOOL_PATH; // /jbr/bin
if (SystemInfo.isMac || SystemInfo.isLinux) {
toolPath = homePath + MAC_TOOL_PATH; // /jbr/Contents/Home/bin
}java也就是说 Linux 被直接和 Mac 划到一起去了,组件找寻 jbr/Contents/Home/bin/keytool,二实际路径是jbr/bin/keytool。
因此我们“伪装路径”,具体方法不做赘述。
移植“模拟器”#
由于鸿蒙的模拟器不开源,我们显然无法强行兼容之,所以这里采用 Oniro Emulator ↗。按照其要求安装配置即可。
效果#



