跳到主要内容
版本:3.5.10

打包可执行归档文件

QWen Max 中英对照 Packaging Executable Archives

该插件可以创建可执行的归档文件(jar 文件和 war 文件),其中包含应用程序的所有依赖项,然后可以通过 java -jar 运行。

打包可执行归档文件由 repackage 目标完成,如下例所示:

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
注意

repackage 目标并非设计为在命令行上单独使用,因为它作用于 package 阶段生成的源 jar(或 war)文件。若要在命令行上使用此目标,必须包含 package 阶段:mvn package spring-boot:repackage

提示

如果你使用的是 spring-boot-starter-parent,该执行已经预先配置了 ID 为 repackage 的执行项,因此只需添加插件定义即可。

上面的示例会重新打包在 Maven 生命周期的 package 阶段构建的 jarwar 归档文件,包括项目中定义的所有 provided 依赖项。如果需要排除其中某些依赖项,可以使用某个 exclude 选项;更多详情请参见依赖排除

原始的(即非可执行的)构件默认会被重命名为 .original,但也可以通过使用自定义的 classifier 来保留原始构件。

备注

目前不支持 maven-war-pluginoutputFileNameMapping 功能。

spring-boot-devtoolsspring-boot-docker-compose 模块默认会自动被排除(你可以通过 excludeDevtoolsexcludeDockerCompose 属性来控制此行为)。为了使该机制在 war 打包方式下正常工作,spring-boot-devtoolsspring-boot-docker-compose 依赖必须设置为 optional 或使用 provided 范围。

该插件会重写你的 manifest 文件,特别是它会管理 Main-ClassStart-Class 条目。如果默认值不起作用,你需要在 Spring Boot 插件中进行配置,而不是在 jar 插件中配置。manifest 中的 Main-Class 由 Spring Boot 插件的 layout 属性控制,如下例所示:

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${start.class}</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

layout 属性的默认值由归档类型(jarwar)决定。可用的布局如下:

  • JAR:常规的可执行 JAR 布局。

  • WAR:可执行 WAR 布局。provided 依赖项会被放置在 WEB-INF/lib-provided 中,以避免在将 war 部署到 servlet 容器时发生任何冲突。

  • ZIPDIR 的别名):类似于使用 PropertiesLauncherJAR 布局。

  • NONE:打包所有依赖项和项目资源,但不包含引导加载器(bootstrap loader)。

分层的 Jar 或 War

一个重新打包的 JAR 文件分别将应用程序的类和依赖项放在 BOOT-INF/classesBOOT-INF/lib 中。类似地,一个可执行的 WAR 文件将应用程序的类放在 WEB-INF/classes 中,依赖项放在 WEB-INF/libWEB-INF/lib-provided 中。在需要从 JAR 或 WAR 文件的内容构建 Docker 镜像的情况下,能够进一步分离这些目录以便将它们写入不同的层是非常有用的。

分层归档文件使用与常规重新打包的 jar 或 war 相同的布局,但包含一个额外的元数据文件,用于描述每一层。

默认情况下,定义了以下层:

  • dependencies 用于所有版本号中不包含 SNAPSHOT 的依赖。

  • spring-boot-loader 用于加载器类。

  • snapshot-dependencies 用于所有版本号中包含 SNAPSHOT 的依赖。

  • application 用于本地模块依赖、应用程序类和资源。

模块依赖项是通过检查当前构建中包含的所有模块来识别的。如果某个模块依赖项仅能被解析,是因为它已被安装到 Maven 的本地缓存中,并且不属于当前构建的一部分,那么它将被识别为普通依赖项。

层的顺序很重要,因为它决定了当应用程序的一部分发生变化时,先前的层有多大可能被缓存。默认顺序为 dependenciesspring-boot-loadersnapshot-dependenciesapplication。最不可能发生变化的内容应最先添加,随后是更可能发生变更的层。

重新打包的归档文件默认包含 layers.idx 文件。要禁用此功能,可以按以下方式操作:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>false</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>
</project>

自定义层配置

根据你的应用需求,你可能需要调整图层的创建方式并添加新的图层。这可以通过一个单独的配置文件来实现,该文件应按如下所示进行注册:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
<configuration>${project.basedir}/src/layers.xml</configuration>
</layers>
</configuration>
</plugin>
</plugins>
</build>
</project>

配置文件描述了如何将一个归档文件划分为多个层,以及这些层的顺序。以下示例展示了如何显式地定义上述默认的排序:

<layers xmlns="http://www.springframework.org/schema/boot/layers"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
https://www.springframework.org/schema/boot/layers/layers-3.5.xsd">
<application>
<into layer="spring-boot-loader">
<include>org/springframework/boot/loader/**</include>
</into>
<into layer="application" />
</application>
<dependencies>
<into layer="application">
<includeModuleDependencies />
</into>
<into layer="snapshot-dependencies">
<include>*:*:*SNAPSHOT</include>
</into>
<into layer="dependencies" />
</dependencies>
<layerOrder>
<layer>dependencies</layer>
<layer>spring-boot-loader</layer>
<layer>snapshot-dependencies</layer>
<layer>application</layer>
</layerOrder>
</layers>

layers XML 格式分为三个部分:

  • <application> 块定义了应用程序类和资源应该如何分层。

  • <dependencies> 块定义了依赖项应该如何分层。

  • <layerOrder> 块定义了各层应写入的顺序。

嵌套的 <into> 块用于 <application><dependencies> 部分中,以声明某一层的内容。这些块按照定义的顺序从上到下依次进行评估。任何未被前面块声明的内容将保持可用,供后续的块考虑。

<into> 块使用嵌套的 <include><exclude> 元素来声明内容。<application> 部分在 include/exclude 表达式中使用 Ant 风格的路径匹配。<dependencies> 部分使用 group:artifact[:version] 模式。它还提供了 <includeModuleDependencies /><excludeModuleDependencies /> 元素,可用于包含或排除本地模块依赖项。

如果没有定义 <include>,则所有内容(未被前面的块声明的部分)都会被考虑。

如果没有定义 <exclude>,则不会应用任何排除规则。

查看上面的 <dependencies> 示例,我们可以看到第一个 <into> 会将所有模块依赖项分配给 application.layer 层。下一个 <into> 会将所有 SNAPSHOT 依赖项分配给 snapshot-dependencies 层。最后一个 <into> 会将剩余的所有内容(在此例中,即所有非 SNAPSHOT 的依赖项)分配给 dependencies 层。

<application> 块具有类似的规则。首先将 org/springframework/boot/loader/** 内容声明给 spring-boot-loader 层,然后将剩余的所有类和资源声明给 application 层。

备注

<into> 块的定义顺序通常与图层的写入顺序不同。因此,必须始终包含 <layerOrder> 元素,并且该元素必须涵盖 <into> 块引用的所有图层。

spring-boot:repackage

org.springframework.boot:spring-boot-maven-plugin:3.5.10

重新打包现有的 JAR 和 WAR 归档文件,使其可以通过命令行使用 java -jar 执行。当使用 layout=NONE 时,也可以仅用于打包一个包含嵌套依赖项的 JAR(不包含主类,因此不可执行)。

必需参数

名称类型默认值
outputDirectoryFile${project.build.directory}

可选参数

名称类型默认值
attachbooleantrue
classifierString
embeddedLaunchScriptFile
embeddedLaunchScriptPropertiesProperties
excludeDevtoolsbooleantrue
excludeDockerComposebooleantrue
excludeGroupIdsString
excludesList
executablebooleanfalse
includeOptionalbooleantrue
includeSystemScopebooleanfalse
includeToolsbooleantrue
includesList
layers[Layers](https://docs.spring.io/spring-boot/3.5/maven-plugin/api/java/org/springframework/boot/maven/Layers.html)
layout[LayoutType](https://docs.spring.io/spring-boot/3.5/maven-plugin/api/java/org/springframework/boot/maven/AbstractPackagerMojo.LayoutType.html)
layoutFactory[LayoutFactory](https://docs.spring.io/spring-boot/3.5/api/java/org/springframework/boot/loader/tools/LayoutFactory.html)
loaderImplementation[LoaderImplementation](https://docs.spring.io/spring-boot/3.5/api/java/org/springframework/boot/loader/tools/LoaderImplementation.html)
mainClassString
outputTimestampString${project.build.outputTimestamp}
requiresUnpackList
skipbooleanfalse

参数详情

attach

将重新打包的归档文件附加到本地 Maven 仓库以进行安装,或部署到远程仓库。如果没有配置 classifier,它将替换正常的 jar 文件。如果已配置了 classifier,使得正常 jar 与重新打包的 jar 不同,则它将与正常 jar 一同附加。当该属性设置为 false 时,重新打包的归档文件将不会被安装或部署。

attach
类型boolean
默认值true
用户属性
1.4.0

classifier

要添加到重新打包的归档文件中的 classifier。如果未指定,则主构件(main artifact)将被重新打包的归档文件替换。如果指定了 classifier,该 classifier 还将用于确定要重新打包的源归档文件:如果已存在具有该 classifier 的构件,则将其用作源并进行替换;如果不存在这样的构件,则使用主构件作为源,而重新打包的归档文件将作为带有该 classifier 的附加构件(supplemental artifact)进行附加。附加构件可以使其与原始构件一同部署,更多详情请参见 Maven 官方文档

classifier
类型java.lang.String
默认值
用户属性
起始版本1.0.0

embeddedLaunchScript

要添加到 JAR 文件开头的内嵌启动脚本(如果该 JAR 是完全可执行的)。如果未指定,则将使用 'Spring Boot' 默认脚本。

embeddedLaunchScript
类型java.io.File
默认值
用户属性
自版本1.3.0

embeddedLaunchScriptProperties

应在嵌入式启动脚本中展开的属性。

embeddedLaunchScriptProperties
类型java.util.Properties
默认值
用户属性
自版本1.3.0

excludeDevtools

从重新打包的归档文件中排除 Spring Boot devtools。

excludeDevtools
类型boolean
默认值true
用户属性spring-boot.repackage.excludeDevtools
起始版本1.3.0

excludeDockerCompose

从重新打包的归档文件中排除 Spring Boot 开发服务。

excludeDockerCompose
类型boolean
默认值true
用户属性spring-boot.repackage.excludeDockerCompose
自版本3.1.0

excludeGroupIds

要排除的 groupId 名称的逗号分隔列表(精确匹配)。

excludeGroupIds
类型java.lang.String
默认值
用户属性spring-boot.excludeGroupIds
起始版本1.1.0

excludes

要排除的构件定义集合。Exclude 元素定义了必需的 groupIdartifactId 组件,以及一个可选的 classifier 组件。当将其配置为属性时,值应以逗号分隔,各组件之间用冒号分隔:groupId:artifactId,groupId:artifactId:classifier

excludes
类型java.util.List
默认值
用户属性spring-boot.excludes
起始版本1.1.0

executable

通过在 JAR 文件前添加一个启动脚本,为 *nix 系统创建一个完全可执行的 JAR 文件。目前,某些工具不支持这种格式,因此你可能无法始终使用此技术。例如,jar -xf 在解压一个已设为完全可执行的 JAR 或 WAR 文件时可能会静默失败。建议仅在你打算直接执行该文件(而不是通过 java -jar 运行或将其部署到 Servlet 容器中)时才启用此选项。

executable
类型boolean
默认值false
用户属性
1.3.0

includeOptional

包含可选依赖项。

includeOptional
类型boolean
默认值true
用户属性
3.5.7

includeSystemScope

包含系统作用域的依赖项。

includeSystemScope
类型boolean
默认值false
用户属性
起始版本1.4.0

includeTools

包含 JAR 工具。

includeTools
类型boolean
默认值true
用户属性
3.3.0

includes

要包含的构件定义集合。Include 元素定义了必需的 groupIdartifactId 组件,以及一个可选的 classifier 组件。当将其配置为属性时,值应以逗号分隔,各组件之间用冒号分隔:groupId:artifactId,groupId:artifactId:classifier

includes
类型java.util.List
默认值
用户属性spring-boot.includes
起始版本1.2.0

layers

层配置,包含禁用层创建、排除层工具 JAR 包,以及提供自定义层配置文件的选项。

layers
类型[org.springframework.boot.maven.Layers](https://docs.spring.io/spring-boot/3.5/maven-plugin/api/java/org/springframework/boot/maven/Layers.html)
默认值
用户属性
2.3.0

layout

归档文件的类型(对应于其内部依赖项的布局方式)。可能的值有 JARWARZIPDIRNONE。默认值根据归档文件类型进行猜测。

layout
类型[org.springframework.boot.maven.AbstractPackagerMojo$LayoutType](https://docs.spring.io/spring-boot/3.5/maven-plugin/api/java/org/springframework/boot/maven/AbstractPackagerMojo.LayoutType.html)
默认值
用户属性spring-boot.repackage.layout
起始版本1.0.0

layoutFactory

如果未显式设置布局,将使用此布局工厂来创建可执行归档文件。第三方可以提供替代的布局实现。

layoutFactory
类型[org.springframework.boot.loader.tools.LayoutFactory](https://docs.spring.io/spring-boot/3.5/api/java/org/springframework/boot/loader/tools/LayoutFactory.html)
默认值
用户属性
起始版本1.5.0

loaderImplementation

应使用的 loader 实现。

loaderImplementation
类型[org.springframework.boot.loader.tools.LoaderImplementation](https://docs.spring.io/spring-boot/3.5/api/java/org/springframework/boot/loader/tools/LoaderImplementation.html)
默认值
用户属性
3.2.0

mainClass

主类的名称。如果未指定,则将使用找到的第一个包含 main 方法的已编译类。

mainClass
类型java.lang.String
默认值
用户属性
起始版本1.0.0

outputDirectory

包含生成的归档文件的目录。

outputDirectory
类型java.io.File
默认值${project.build.directory}
用户属性
起始版本1.0.0

outputTimestamp

用于可重现输出归档条目的时间戳,可以是 ISO 8601 格式(yyyy-MM-dd’T’HH:mm:ssXXX),也可以是表示自 Unix 纪元以来秒数的 int

outputTimestamp
类型java.lang.String
默认值${project.build.outputTimestamp}
用户属性
2.3.0

requiresUnpack

一个必须从 uber jar 中解压出来的库列表,以便运行。将每个库指定为一个 <dependency>,包含 <groupId><artifactId>,它们将在运行时被解压。

requiresUnpack
类型java.util.List
默认值
用户属性
起始版本1.1.0

skip

跳过执行。

skip
类型boolean
默认值false
用户属性spring-boot.repackage.skip
起始版本1.2.0

示例

Custom Classifier

默认情况下,repackage 目标会用重新打包的构件替换原始构件。这对于代表应用程序的模块来说是合理的行为,但如果你的模块被用作另一个模块的依赖项,则需要为重新打包的构件提供一个 classifier。原因在于应用程序类被打包在 BOOT-INF/classes 中,因此依赖该模块的其他模块无法加载重新打包后的 JAR 中的类。

如果情况如此,或者你更倾向于保留原始构件并使用不同的 classifier 附加重新打包的构件,请按以下示例配置插件:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

如果你使用的是 spring-boot-starter-parent,那么 repackage 目标会在 ID 为 repackage 的执行中自动运行。在这种配置下,只需指定配置部分,如下例所示:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

此配置将生成两个构件:原始构件和由 repackage 目标生成的重新打包的对应构件。两者都会被透明地安装/部署。

如果你希望以与主构件相同的方式重新打包一个次要构件,也可以使用相同的配置。以下配置会安装/部署一个带有 task 分类器的构件,其中包含重新打包后的应用程序:

<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
<phase>package</phase>
<configuration>
<classifier>task</classifier>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>task</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

由于 maven-jar-pluginspring-boot-maven-plugin 都在相同的阶段执行,因此必须先定义 jar 插件(以确保它在 repackage 目标之前运行)。同样地,如果你使用的是 spring-boot-starter-parent,可以简化如下:

<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>default-jar</id>
<configuration>
<classifier>task</classifier>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<configuration>
<classifier>task</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

自定义名称

如果你需要重新打包的 JAR 文件在本地使用不同于项目 artifactId 属性所定义的名称,请使用标准的 finalName,如下例所示:

<project>
<build>
<finalName>my-app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

此配置将在 target/my-app.jar 中生成重新打包的构件。

本地重新打包的构件

默认情况下,repackage 目标会用可执行的构件替换原始构件。如果你只需要部署原始 jar,同时仍能使用常规文件名运行你的应用,请按如下方式配置插件:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<attach>false</attach>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

此配置会生成两个产物:原始产物和由 repackage 目标生成的可执行产物。只有原始产物会被安装/部署。

自定义布局

Spring Boot 使用在附加的 jar 文件中定义的自定义布局工厂(该 jar 文件作为构建插件的依赖项提供)对该项目的 jar 文件进行重新打包:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<layoutFactory implementation="com.example.CustomLayoutFactory">
<customProperty>value</customProperty>
</layoutFactory>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>custom-layout</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

布局工厂作为 LayoutFactory(来自 spring-boot-loader-tools)的一个实现,在 pom 中显式指定。如果插件 classpath 上仅有一个自定义的 LayoutFactory,并且它已在 META-INF/spring.factories 中列出,则无需在插件配置中显式设置它。

如果显式设置了 layout,则 layout factories 始终会被忽略。

依赖排除

默认情况下,repackagerun 目标都会包含项目中定义的任何 provided 依赖。Spring Boot 项目应将 provided 依赖视为运行应用程序所需的“容器”依赖。一般来说,Spring Boot 项目不会被用作依赖项,因此不太可能包含任何 optional 依赖。当项目确实包含可选依赖时,这些依赖也会被 repackagerun 目标包含进来。

其中一些依赖项可能完全不需要,应从可执行 jar 中排除。为了保持一致性,在运行应用程序时也不应包含这些依赖项。

有两种方式可以排除某个依赖项,使其不被打包或在运行时使用:

  • 排除由 groupIdartifactId 标识的特定构件,如果需要,还可以选择性地指定 classifier

  • 排除属于指定 groupId 的任何构件。

以下示例排除了 com.example:module1,且仅排除该构件:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>com.example</groupId>
<artifactId>module1</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

此示例排除了属于 com.example 组的所有构件:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeGroupIds>com.example</excludeGroupIds>
</configuration>
</plugin>
</plugins>
</build>
</project>

JAR 工具

当创建分层的 jar 或 war 时,spring-boot-jarmode-tools jar 将作为依赖项添加到你的归档文件中。当该 jar 位于 classpath 上时,你可以以一种特殊模式启动应用程序,该模式允许引导代码运行与你的应用程序完全不同的内容,例如,用于提取分层内容的工具。如果你希望排除此依赖项,可以按以下方式操作:

<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeTools>false</includeTools>
</configuration>
</plugin>
</plugins>
</build>
</project>

自定义层配置

默认设置将依赖项分为 snapshot 和非 snapshot 两类,但你可能有更复杂的规则。例如,你可能希望将项目中公司特定的依赖项隔离到一个专用的层中。以下 layers.xml 配置展示了一种这样的设置:

<layers xmlns="http://www.springframework.org/schema/boot/layers"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
https://www.springframework.org/schema/boot/layers/layers-3.5.xsd">
<application>
<into layer="spring-boot-loader">
<include>org/springframework/boot/loader/**</include>
</into>
<into layer="application" />
</application>
<dependencies>
<into layer="snapshot-dependencies">
<include>*:*:*SNAPSHOT</include>
</into>
<into layer="company-dependencies">
<include>com.acme:*</include>
</into>
<into layer="dependencies"/>
</dependencies>
<layerOrder>
<layer>dependencies</layer>
<layer>spring-boot-loader</layer>
<layer>snapshot-dependencies</layer>
<layer>company-dependencies</layer>
<layer>application</layer>
</layerOrder>
</layers>

上述配置会创建一个额外的 company-dependencies 层,其中包含所有 groupIdcom.acme 的库。