Apache Flex 4.16.1 mxmlc 与 compc 编译器详解

深入解析 Apache Flex 4.16.1 的 mxmlc 与 compc 编译器:编译模型与配置加载链、常用选项实测对比、flex-config.xml 关键段、file 与 swfdump 产物验证、SWC 库打包、fcsh 增量编译、JVM 堆参数调优与 JDK 版本兼容实测结论,全部命令回显均实测通过。

Apache FlexApache Flex2026/10/15

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
工具作用本文覆盖
mxmlcMXML/AS3 → SWF 应用是
compcAS3/MXML 组件 → SWC 库是
fcsh常驻编译 Shell,增量编译是(第七章)
asdoc从源码生成 API 文档(HTML)结论性引用(需 JDK 8,见第九章)
swfdump反汇编 SWF,验证产物是(第五章)
optimizerSWF 优化瘦身结论性引用(模块化实战篇)
amxmlc/acompc/aasdocAIR 目标版的对应工具(加 air-config)否
fdbFlash 调试器否
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>

加载链总结:

  1. 内置编译器默认值(lib/mxmlc.jar 内嵌)。
  2. +flexlib=$FLEX_HOME/frameworks 指向的 frameworks/flex-config.xml(启动脚本第 69 行写死传入;AIR 目标时换成 air-config)。
  3. -load-config 额外指定的文件(可多次,后者覆盖前者)。
  4. 命令行选项(优先级最高,+= 追加、= 覆盖)。
  5. -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)取最后一次值,这就是「不改脚本调堆」的通道。两个覆盖入口:

  1. 会话级/构建级:export SETUP_SH_VMARGS="-Xms256m -Xmx512m" 后再调 mxmlc。
  2. 脚本级:直接编辑 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),结论如下表(均为实测):

环境JDKmxmlccompcasdoc实测证据
Ubuntu 22.04OpenJDK 1.8.0_504正常(511462 字节)正常(2141 字节)正常生成 HTML快速入门篇全部回显
Ubuntu 22.04OpenJDK 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 关键项。

十一、参考链接

  1. Apache Flex 官方主页:https://flex.apache.org/
  2. Flex 4.16.1 发行说明:https://downloads.apache.org/flex/4.16.1/RELEASE_NOTES
  3. mxmlc/compc 编译器使用文档(Apache Flex Wiki):https://cwiki.apache.org/confluence/display/FLEX/Compiler
  4. SWF 文件格式规范(file/swfdump 验证依据):https://www.adobe.com/devnet/swf.html
  5. fcsh 说明:https://cwiki.apache.org/confluence/display/FLEX/Using+fcsh
  6. 本专栏《Apache Flex 4.16.1 快速入门与集成示例》(最小应用与 SWC 引用的完整源文件)