Apache Flex 4.16.1 mxmlc 与 compc 编译器详解
深入解析 Apache Flex 4.16.1 的 mxmlc 与 compc 编译器:编译模型与配置加载链、常用选项实测对比、flex-config.xml 关键段、file 与 swfdump 产物验证、SWC 库打包、fcsh 增量编译、JVM 堆参数调优与 JDK 版本兼容实测结论,全部命令回显均实测通过。
Apache Flex 4.16.1 mxmlc 与 compc 编译器详解
目标:吃透 Apache Flex 4.16.1 的两大编译器——mxmlc(应用编译)与 compc(库编译):理清命令行到 flex-config.xml 的配置加载链与覆盖关系,实测对比常用选项的行为差异,掌握 file/swfdump 两级产物验证,用好 fcsh 增量编译与 JVM 堆参数,并把 JDK 8/11/17 的兼容性结论落到纸面。以下全部命令与回显均已在 Ubuntu 22.04(x86_64)、OpenJDK 1.8.0_504、Apache Flex SDK 4.16.1(build 20171115)实测通过。
mxmlc 与 compc 都是 Java 进程:mxmlc 把 MXML + ActionScript 3 编译成可运行的 SWF 应用,compc 把 ActionScript 3 源码(可选含 MXML 组件)打包成 SWC 组件库。两者共享同一套 -compiler.* 选项体系与同一份默认配置文件,区别在于「输出目标」与「输入组织方式」——应用编译以一个根 MXML/AS 文件为入口做可达性分析,库编译以 -include-classes 显式声明要进包的类。理解了这条主线,两个编译器的几十个选项就能按「输入、类路径、输出、验证」四类归位。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- JDK:OpenJDK 1.8.0_504(
java -version实测) - SDK:Apache Flex 4.16.1(build 20171115),安装于
/opt/apache-flex-sdk-4.16.1-bin,playerglobal 32.0 - 示例工程:
/root/hello(HelloWorld 应用,见快速入门篇)、/root/libdemo(StringUtils 工具库) - 验证工具:
file命令 + SDK 自带bin/swfdump+unzip -l
确认基线(实时回显):
mxmlc -version
compc -version
Version 4.16.1 build 20171115
Version 4.16.1 build 20171115
二、编译器与工具链总览
2.1 bin 目录工具清单
SDK 的 bin/ 目录即全部命令行工具(* 为 Unix 启动脚本,配对的 .bat 供 Windows 用):
ls /opt/apache-flex-sdk-4.16.1-bin/bin/
aasdoc
aasdoc.bat
acompc
acompc.bat
amxmlc
amxmlc.bat
asc
asc.bat
asdoc
asdoc.bat
compc
compc.bat
copylocale
copylocale.bat
digest
digest.bat
fcsh
fcsh.bat
fdb
fdb.bat
fontswf
fontswf.bat
mxmlc
mxmlc.bat
optimizer
optimizer.bat
swcdepends
swcdepends.bat
swfdump
swfdump.bat
| 工具 | 作用 | 本文覆盖 |
|---|---|---|
| mxmlc | MXML/AS3 → SWF 应用 | 是 |
| compc | AS3/MXML 组件 → SWC 库 | 是 |
| fcsh | 常驻编译 Shell,增量编译 | 是(第七章) |
| asdoc | 从源码生成 API 文档(HTML) | 结论性引用(需 JDK 8,见第九章) |
| swfdump | 反汇编 SWF,验证产物 | 是(第五章) |
| optimizer | SWF 优化瘦身 | 结论性引用(模块化实战篇) |
| amxmlc/acompc/aasdoc | AIR 目标版的对应工具(加 air-config) | 否 |
| fdb | Flash 调试器 | 否 |
| asc | 低层 ActionScript 编译器 | 否 |
2.2 配置加载链与覆盖关系
每次编译,编译器按固定顺序做「加载默认配置 → 应用命令行覆盖」,实测可把链路整体导出来看:
cd /root/hello && mxmlc -dump-config eff-config.xml >/dev/null 2>&1
grep -n '<swf-version>\|<target-player>' eff-config.xml | head -2
grep -n '<path-element>libs/player' eff-config.xml | head -2
498: <swf-version>43</swf-version>
500: <target-player>32.0</target-player>
68: <path-element>libs/player/{targetPlayerMajorVersion}.{targetPlayerMinorVersion}/playerglobal.swc</path-element>
144: <path-element>libs/player/{targetPlayerMajorVersion}.{targetPlayerMinorVersion}</path-element>
加载链总结:
- 内置编译器默认值(
lib/mxmlc.jar内嵌)。 +flexlib=$FLEX_HOME/frameworks指向的frameworks/flex-config.xml(启动脚本第 69 行写死传入;AIR 目标时换成 air-config)。-load-config额外指定的文件(可多次,后者覆盖前者)。- 命令行选项(优先级最高,
+=追加、=覆盖)。 -dump-config把「全部生效值」导出为一份 XML——排查「到底哪个配置生效了」用它最直接。
2.3 启动脚本与 JVM 入口
bin/mxmlc 是个 Shell 包装脚本,核心两行(实测 grep 原文):
grep -n VMARGS /opt/apache-flex-sdk-4.16.1-bin/bin/mxmlc
67:VMARGS="-Xmx384m -Dsun.io.useCanonCaches=false -Djava.util.Arrays.useLegacyMergeSort=true "
69:java $VMARGS $D32 $SETUP_SH_VMARGS -jar "$FLEX_HOME/lib/mxmlc.jar" +flexlib="$FLEX_HOME/frameworks" "$@"
三个关键点:默认堆上限 384 MB(第八章给调优方法);$SETUP_SH_VMARGS 排在 $VMARGS 之后,重复的 -Xmx 由 JVM 按最后一次取值,环境变量因此能覆盖脚本默认值;+flexlib 是编译器命令行专用参数,指明框架库根目录。
三、mxmlc 常用选项详解
3.1 选项速查表(全部实测)
| 选项 | 作用 | 实测效果 |
|---|---|---|
| 无参 | 用 flex-config.xml 默认值编译 | HelloWorld.mxml → 511462 字节,SWF v43 |
-o <file> | 指定输出文件名 | -o ant-hello.swf → 511460 字节 |
-target-player=<x> | 目标 Flash Player 版本 | 缺 playerglobal 时报 unable to open(3.2 节) |
-swf-version=<n> | 强制 SWF 版本号 | -swf-version=32 → file 显示 version 32 |
-library-path+=<dir> | 追加编译期库目录 | -library-path+=libs 引入 SWC,swfdump -abc 验证命中 17 处 |
-static-link-runtime-shared-libraries=<bool> | 框架库静态嵌入或 RSL 动态加载 | false → 产物从 511462 缩到 58279 字节(模块化实战篇) |
-dump-config <file> | 导出全部生效配置 | 2.2 节链路排查 |
-help list | 列出全部选项 | 3.3 节 |
3.2 -target-player 与 playerglobal 的绑定关系
-target-player 决定三件事:API 可见性(用 playerglobal 的哪个版本做类型检查)、SWF 版本号(32.0 → 43)、运行时特性。它必须与 frameworks/libs/player/<版本>/playerglobal.swc 成对出现,实测指向未安装版本时:
cd /root/hello && mxmlc -target-player=11.1 HelloWorld.mxml 2>&1 | grep Error
/opt/apache-flex-sdk-4.16.1-bin/frameworks/flex-config.xml(65): Error: unable to open 'libs/player/11.1/playerglobal.swc'
不传该参数时使用 flex-config.xml 第 25 行的默认值(ant 安装器已设为 32.0)。同一 SDK 通过并列放置多版本 playerglobal 目录来支持多目标(布局验证见安装指南篇第八章)。
3.3 选项发现机制
全量选项用 -help list 列出(节选):
mxmlc -help list 2>&1 | head -14
Apache Flex Compiler (mxmlc)
Version 4.16.1 build 20171115
Copyright 2017 The Apache Software Foundation.
-benchmark
-broker-local-connection
-broker-product-manager
-compiler.accessible
-compiler.actionscript-file-encoding <string>
-compiler.advanced-telemetry
-compiler.compress
-compiler.context-root <context-path>
-compiler.debug
模糊查询单个选项:mxmlc -help swf-version 直接打印该选项的语义与默认值——记不住准确拼写时先 -help list 搜索再精查。
四、flex-config.xml 关键段解读
frameworks/flex-config.xml 是默认配置的单一来源,ant 安装器修正后本文环境实测生效的三段:
目标版本段(第 25、28 行):
<target-player>32.0</target-player>
<swf-version>43</swf-version>
库路径段(library-path 第 144 行附近):
<path-element>libs/player/{targetPlayerMajorVersion}.{targetPlayerMinorVersion}</path-element>
大括号内的占位符在编译时按当前 target-player 展开为 libs/player/32.0,这就是 3.2 节报错信息里路径的由来;框架库本体(libs/framework.swc、libs/spark.swc、libs/mx.swc、libs/textLayout.swc、libs/osmf.swc 等)固定在默认 library-path 中。
RSL 策略段(runtime-shared-library-path 系列):
每个框架 SWC 都声明了对应的 RSL 文件名与 digest,例如 framework.swc 绑定 framework_4.16.1.20171115.swf。这些声明平时静默——只有命令行把静态链接关掉时才起作用(见模块化实战篇实测:-static-link-runtime-shared-libraries=false 让产物体积从 511 KB 降到 58 KB,swfdump 里能直接看到 RSL 引用)。
修改配置的三种方式按侵入度排序:命令行参数(临时)、项目级 -load-config my.xml(可先复制 flex-config.xml 改差异项)、直接改 flex-config.xml(全局生效,升级 SDK 会丢,不推荐)。
五、产物验证:file 与 swfdump
编译没有报错不等于产物正确,工程上用两级验证:
第一级:file 魔数识别。 确认文件确实是 SWF 且版本号符合预期:
cd /root/hello && mxmlc -swf-version=32 HelloWorld.mxml 2>&1 | tail -1 && file HelloWorld.swf
/root/hello/HelloWorld.swf (511477 bytes)
HelloWorld.swf: Macromedia Flash data (compressed), version 32
(-swf-version 可独立于 target-player 强制版本号——降级场景用它做兼容性实验很方便,实测 32 可用。)
第二级:swfdump 结构解析。 解出头部与标签流:
/opt/apache-flex-sdk-4.16.1-bin/bin/swfdump HelloWorld.swf 2>/dev/null | grep -E "swf xmlns|ProductInfo|FrameLabel"
<swf xmlns='http://macromedia/2003/swfx' version='43' framerate='24' size='8000x4000' compressed='true' >
<ProductInfo product='Apache Flex' edition='' version='4.16' build='20171115' compileDate='10/15/26 7:56 AM'/>
<FrameLabel label='_HelloWorld_mx_managers_SystemManager'/>
<FrameLabel label='HelloWorld'/>
(回显取自默认参数编译的产物:version 43、帧率 24、尺寸 8000x4000 twips 即 400x200 像素。)
第三级(库验证专用):swfdump -abc。 对 SWC 引用产物列出 AVM2 类清单,确认库类真的被静态嵌入(快速入门篇实测:SwcUse.swf 中 com.example 命中 17 处)。此验证对「编译成功但类没进包」的假成功是唯一防线。
六、compc 与 SWC 库打包
6.1 最小 SWC
源文件 /root/libdemo/src/com/example/StringUtils.as(三个静态方法:isBlank/trim/repeat),一条命令打包:
cd /root/libdemo && compc -source-path src -include-classes com.example.StringUtils -output libs/strutils.swc 2>&1 | tail -1
ls -l libs/
/root/libdemo/libs/strutils.swc (2141 bytes)
total 4
-rw-r--r-- 1 root root 2141 Oct 15 08:23 strutils.swc
选项拆解:-source-path 声明 AS3 源码根(包名与目录结构对应);-include-classes 显式列出进包的类(可达性分析从这些类出发,未引用到的辅助类不会进包,需要进包就再列出);-output 指定产物路径。
6.2 SWC 的物理结构
unzip -l libs/strutils.swc | tail -5
--------- ---------- ----- ----
1224 2026-10-15 08:23 catalog.xml
1347 2026-10-15 08:23 library.swf
--------- -------
2571 2 files
catalog.xml 是清单(类名、版本、digest),library.swf 是字节码本体。应用端引用方式与库更新流程实测链路:mxmlc -library-path+=libs SwcUse.mxml 编译 → swfdump -abc 验证类嵌入 → 改库重打 SWC 后重新编译应用即可(SWC 在编译期只做类型检查与复制字节码,无缓存陷阱)。
compc 对应的 AIR 版本工具是 acompc,语义同 compc,仅配置切换为 air-config。
七、fcsh 增量编译
fcsh(Flex Compiler Shell)是常驻编译进程:JVM 只启动一次、框架库只解析一次,对反复编译同一工程(改一行代码重编)的场景节省大量启动开销。用法是把编译命令发给它的交互提示符(实测会话,管道喂入):
cd /root/hello && printf "mxmlc -o fcsh-hello.swf HelloWorld.mxml\ncompile 1\nquit\n" | $FLEX_HOME/bin/fcsh 2>&1 | grep -vE "^Loading configuration file /opt/apache-flex-sdk-4.16.1-bin/frameworks/flex-config.xml$" | head -9
Apache Flex Compiler SHell (fcsh)
Version 4.16.1 build 20171115
Copyright 2017 The Apache Software Foundation.
(fcsh) fcsh: Assigned 1 as the compile target id
fcsh-hello.swf (511460 bytes)
(fcsh) Loading configuration file /opt/apache-flex-sdk-4.16.1-bin/frameworks/flex-config.xml
Nothing has changed since the last compile. Skip...
fcsh-hello.swf (511460 bytes)
要点三条:首次编译返回 Assigned 1 as the compile target id,目标编号 1 就是一次完整编译的句柄;compile 1 重跑该句柄,源码没变时输出 Nothing has changed since the last compile. Skip...(实测跳过重编),变了才真正重编;多工程共用一个 fcsh 进程时各自分配独立编号。CI 流水线里不建议用 fcsh(常驻进程的生命周期与 CI 语义不合),开发调试阶段受益最大。
八、JVM 参数与内存调优
8.1 参数位置与覆盖机制
如 2.3 节所见,bin/mxmlc 第 67 行写死 VMARGS="-Xmx384m -Dsun.io.useCanonCaches=false -Djava.util.Arrays.useLegacyMergeSort=true ",第 69 行的实际调用序列是 java $VMARGS $D32 $SETUP_SH_VMARGS——SETUP_SH_VMARGS 环境变量排在后面,对重复出现的 JVM 参数(如 -Xmx)取最后一次值,这就是「不改脚本调堆」的通道。两个覆盖入口:
- 会话级/构建级:
export SETUP_SH_VMARGS="-Xms256m -Xmx512m"后再调 mxmlc。 - 脚本级:直接编辑
bin/mxmlc第 67 行(SDK 升级会丢失,不推荐)。
8.2 生效验证(实测)
用 -XX:+PrintFlagsFinal 让 JVM 打印最终生效的堆参数:
cd /root/hello && SETUP_SH_VMARGS="-Xms256m -Xmx512m -XX:+PrintFlagsFinal" mxmlc HelloWorld.mxml 2>&1 | grep -E "InitialHeapSize|MaxHeapSize"
uintx InitialHeapSize := 268435456 {product}
uintx MaxHeapSize := 536870912 {product}
268435456 = 256 MB,536870912 = 512 MB,覆盖机制确认生效。推荐构建脚本统一固定 SETUP_SH_VMARGS="-Xms256m -Xmx512m"。
8.3 各工具的默认堆与已知差异
实测 grep 三个脚本的 VMARGS 原文:mxmlc/compc 默认 -Xmx384m;asdoc 默认 -Xmx1024m(生成文档的内存开销大一个量级)。中大型工程(几百个类 + 大量嵌入资源)可能撑爆 384 MB,报 java.lang.OutOfMemoryError: Java heap space 时先按 8.2 节把堆调到 512 MB 再看。
九、JDK 版本兼容实测结论
在三个独立环境各完整编译一次同一工程(HelloWorld.mxml,target-player 32.0),结论如下表(均为实测):
| 环境 | JDK | mxmlc | compc | asdoc | 实测证据 |
|---|---|---|---|---|---|
| Ubuntu 22.04 | OpenJDK 1.8.0_504 | 正常(511462 字节) | 正常(2141 字节) | 正常生成 HTML | 快速入门篇全部回显 |
| Ubuntu 22.04 | OpenJDK 11.0.32 | 正常(511451 字节) | 未测 | 未测 | 切 alternatives 后重编成功 |
| JDK 17 环境 | Temurin 17.0.20 | 正常(511452 字节) | 正常(2166 字节) | 失败 | 报错回显见下 |
asdoc 在 JDK 17 上的失败回显(实测原文):
-Xbootclasspath/p is no longer a supported option.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
根因:bin/asdoc 第 70 行给 JVM 传了 -Xbootclasspath/p:"$FLEX_HOME/lib/xalan.jar"(grep 实测原文),该选项自 JDK 9 起被移除,JVM 直接拒绝启动。mxmlc/compc 的启动脚本不含该选项,因此不受影响。
落地建议:JDK 8 作为全工具链基线(全部功能可用);mxmlc/compc 可以放心跑在 11/17;需要 asdoc 时单独把 JAVA_HOME 切回 8,其余流程不动。另注意编译器对配置完整性的要求与 JDK 无关——「unable to open '{playerglobalHome}/32.0'」这类报错在 8/17 上表现一致,根因都是跳过了 ant 安装器的配置修正。
十、常见问题(FAQ)
Q1:-library-path+=libs 与 -library-path libs 有什么区别?
+= 是追加(保留默认框架库路径),= 是覆盖(默认库全部失效,编译直接报大量找不到 mx.controls 等类型)。引用自己的 SWC 永远用 +=。
Q2:编译产物每次字节数都有细微差异(511459/511460/511462)?
正常。SWF 内嵌编译时间戳等元数据,跨时间编译产物不同属预期行为;file 版本号、swfdump 结构、类清单才是稳定性判定依据。
Q3:-swf-version 与 -target-player 谁说了算?
-target-player 决定 API 与默认 SWF 版本(32.0 → 43),-swf-version 强制改写版本号字节。实测 -swf-version=32 单独生效时 file 显示 version 32。发布校验以 file + swfdump 实测值为准,不要只信配置文件。
Q4:编译器堆内存不足报 OutOfMemoryError?
先确认不是资源问题:-dump-config 导出生效配置,核对嵌入资源是否异常大;然后按第八章把 SETUP_SH_VMARGS 设为 -Xms256m -Xmx512m(PrintFlagsFinal 实测生效)重编。再不够就升级脚本内 VMARGS(改前备份)。
Q5:fcsh 报 compile 1 不是有效目标?
每个 fcsh 进程独立编号:新启动的进程里不存在编号 1,必须先在该进程里下发 mxmlc 命令拿到自己的目标编号。管道方式喂命令时,整段命令必须与首次编译在同一进程内(见第七章实测写法)。
Q6:怎么确认编译器加载的到底是哪份配置文件?
每次编译输出的第一行就是答案:Loading configuration file /opt/apache-flex-sdk-4.16.1-bin/frameworks/flex-config.xml。用 -load-config 指定文件时同样会打印对应路径;要更彻底就 -dump-config 导出后 grep 关键项。
十一、参考链接
- Apache Flex 官方主页:https://flex.apache.org/
- Flex 4.16.1 发行说明:https://downloads.apache.org/flex/4.16.1/RELEASE_NOTES
- mxmlc/compc 编译器使用文档(Apache Flex Wiki):https://cwiki.apache.org/confluence/display/FLEX/Compiler
- SWF 文件格式规范(file/swfdump 验证依据):https://www.adobe.com/devnet/swf.html
- fcsh 说明:https://cwiki.apache.org/confluence/display/FLEX/Using+fcsh
- 本专栏《Apache Flex 4.16.1 快速入门与集成示例》(最小应用与 SWC 引用的完整源文件)