手把手教你在Windows上编译Swift工具链_windows 无法编译swift-程序员宅基地

技术标签: swift  

2020/5/27更新:大家现在可以直接从Saleem Abdulrasool的Azure Pipeline中下载打包好的Swift Windows SDK了,在他的GitHub中找到swift-build项目,里面的CI链接里找到Artifacts就可以直接下载(类似windows-toolchain-amd64.msi这样的文件)。考虑到5.3版本的Swift将支持Windows平台,因此以下文章也许已经成为历史,大家看看就好了。

----------------------------------------- 以下是原文 -----------------------------------------

大家五一节快乐。五一这两天杭州非常的热,吉祥宅在家里也不想开发引擎,便换了个模式,了解了一下由Apple于2014年推出的Swift语言。作者身边大多数人对Swift了解得并不多,对其的印象其实和ObjC差不多:由Apple开发,且只能用在Apple家族的设备上。作者在之前也一直抱有这个想法,但是这一次仔细地研究了一下,发现并不是这样。事实上,Swift是一门完全开源免费的语言,以Apache 2.0(包括运行库例外)协议授权,并且和微软的C#一样提供跨平台支持,并不是只能用在苹果自家的设备上。然而,尽管Swift自称是跨平台语言,作者却从未见过有人在Windows上使用Swift编程,而在Swift的官网上,我们可以看到其也只是提供了XCode和Ubuntu系统的下载链接,似乎在Windows平台上,除了使用微软的Linux Subsystem之外,没有办法直接使用Swift。

但是经过又一番搜索,作者发现Swift其实是可以在Windows上使用的,只不过官方并没有提供Windows的二进制下载包,用户得自己在Windows上编译Swift的工具链。事实上,Swift在很早的时候就已经可以用于Windows和Android系统上,这两个系统上的工具链是由社区(主要是Google的大佬们)维护的,官网对此只字未提,作者也只能在Swift语言的GitHub页面中找到Swift在Windows上编译的一些零碎的教程。在Windows上编译和使用Swift需要进行十分繁琐的配置环境、编译、部署工作,作者花了昨天一个下午加上今天一个早上才真正完成整体的环境搭建,但是它确实能够运行起来,并且达到我们“在Windows上用Swift开发原生Windows程序”的目的。由于GitHub的官方教程有一些细节问题,并且作者在自己作死编译Swift的时候又遇到了一系列新的问题,耽误了很多时间,而作者找遍了全网也没有找到第二篇关于在Windows上编译Swift的教程,因此作者将自己在Windows上编译Swift的实践结果记录在这篇文章中,希望能够让有同样需求的大家少踩一些已经踩过的坑。

本文主要分为以下三个部分:

  1. 前期准备(下载项目,准备编译环境)
  2. 按顺序编译各个Swift组件
  3. 用一个简单的swift文件测试一下

官方原版教程的在这里:

apple/swift​github.com图标

前期准备

首先我们需要安装用于编译Swift的开发环境,包括以下内容:

  1. 安装Visual Studio 2019或者更新版本。2017也许可以,这里没有测试过。
  2. 在VS Installer的工作负载中,在“桌面应用和移动应用”中,选择“使用C++的桌面开发”和“通用Windows平台开发”。作者建议直接把这两个勾上,按照默认配置安装就完事,如果需要单独选的话,至少需要选择以下几项:
    1. MSVC v14x 工具链。
    2. Windows 10 SDK 版本在10.0.17763以上。
    3. CMake和Ninja,在勾选了“适用于Windows的C++ CMake工具”以后会自动安装,如果已有的话,将其添加到Path中也一样有效。
    4. ATL(适用于最新v14x生成工具的C++ ATL)。
    5. Google Test 测试适配器。
  3. 在单个组件里面,单独勾选安装Python 2.7,注意32位的路径是C:\Python27\,64位的路径是C:\Python27amd64\,在后面执行命令的时候如果有提示找不到Python,请检查是否需要添加或者移除amd64后缀。

在安装好所有的以后,按照官方文档的步骤打开PC的开发者模式。

如果您的系统为中文系统,还需要进行一步额外的操作,即在控制面板->区域->管理->非Unicode程序的语言 中,勾选“"Beta 版" : 使用Unicode UTF-8提供全球语言支持”,并且将当前系统区域设置改为“英语(美国)”。由于Swift的源代码使用UTF-8编码,并且包含特殊字符(作者不知道为啥要这么做),因此如果您在这里不配置的话会导致因为字符无法识别而导致的编译失败。

在完成以上操作后,我们需要创建一个文件夹,以放置所有Swift工具链相关的内容,以下的代码和说明均以D:\Swift\文件夹为例,如您的文件夹位于别处,可以手动替换所有的D:\Swift\路径至您的路径积可。

在创建完目录以后,我们需要进入到目录下,并克隆所有需要的Swift项目源代码。使用管理器权限打开VS自带的x64 Native Tools Command Prompt(直接在开始菜单中搜索“x64”应该就会出现),然后执行以下代码:

git clone --depth=1 https://github.com/apple/llvm-project --branch swift/master toolchain
git clone -c core.autocrlf=input -c core.symlinks=true --depth=1 https://github.com/apple/swift toolchain/swift
git clone --depth=1 https://github.com/apple/swift-cmark toolchain/cmark
git clone --depth=1 https://github.com/apple/swift-corelibs-libdispatch swift-corelibs-libdispatch
git clone --depth=1 https://github.com/apple/swift-corelibs-foundation swift-corelibs-foundation
git clone --depth=1 https://github.com/apple/swift-corelibs-xctest swift-corelibs-xctest
git clone --depth=1 https://github.com/apple/swift-llbuild llbuild
git clone --depth=1 https://github.com/apple/swift-tools-support-core swift-tools-support-core
git clone -c core.autocrlf=input --depth=1 https://github.com/apple/swift-package-manager swiftpm
git clone --depth=1 https://github.com/compnerd/swift-build swift-build

由于我们只是需要下载文件,所以我在所有克隆请求中都加了--depth=1来加快下载速度。全部下载完成后的目录应该包括以下目录:

  • toolchain
    • swift
    • cmark
  • swift-corelibs-libdispatch
  • swift-corelibs-foundation
  • swift-corelibs-xctest
  • llbuild
  • swift-tools-support-core
  • swiftpm
  • swift-build

然后我们需要下载Swift的依赖库。在D:\Swift\下新建一个目录Library,然后按照官方文档的支持去AZure页面下载最新的daily build文件。

以SQLite为例,我们找到Runs中最新一次成功的build,点开详情也没,在Stages中找到windows,在弹出的详情页面中,我们可以在右边黑色日志窗口的最后一行文字中看到类似`1 artifact produced`的提示。点击artifact进入Artifacts的页面,选择合适的版本,这里是sqlite-windows-x64,然后点击右方的下载按钮即可下载打包好的压缩包。

在解压所有压缩包以后,Library目录应该如下所示:

/Library
  ┝ icu-64
  │   ┕ usr/...
  ├ libcurl-development
  │   ┕ usr/...
  ├ libxml2-development
  │   ┕ usr/...
  ├ sqlite-3.30.0
  │   ┕ usr/...
  ┕ zlib-1.2.11
      ┕ usr/...

需要注意,如果您下载的库的版本号或者名字与上图不符,请以实际下载的为准,并在后面的脚本中修改对应的路径以匹配实际的版本号。

在完成依赖库下载以后,按照官方文档的指示将模块正确设置:

mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\ucrt\module.modulemap" D:\Swift\toolchain\swift\stdlib\public\Platform\ucrt.modulemap
mklink "%UniversalCRTSdkDir%\Include\%UCRTVersion%\um\module.modulemap" D:\Swift\toolchain\swift\stdlib\public\Platform\winsdk.modulemap
mklink "%VCToolsInstallDir%\include\module.modulemap" D:\Swift\toolchain\swift\stdlib\public\Platform\visualc.modulemap
mklink "%VCToolsInstallDir%\include\visualc.apinotes" D:\Swift\toolchain\swift\stdlib\public\Platform\visualc.apinotes

编译组件

首先开始编译LLVM和Swift核心编译器。在确认一切准备都妥当以后,执行以下命令:

md "D:\Swift\b\toolchain"
cmake -B "D:\Swift\b\toolchain" ^
  -C D:\Swift\swift-build\cmake\caches\windows-x86_64.cmake ^
  -C D:\Swift\swift-build\cmake\caches\org.compnerd.dt.cmake ^
  -D CMAKE_BUILD_TYPE=Release ^
  -D LLVM_ENABLE_ASSERTIONS=YES ^
  -D LLVM_ENABLE_PROJECTS="clang;clang-tools-extra;cmark;swift;lldb;lld" ^
  -D LLVM_EXTERNAL_PROJECTS="cmark;swift" ^
  -D SWIFT_PATH_TO_LIBDISPATCH_SOURCE=D:\Swift\swift-corelibs-libdispatch ^
  -D LLVM_ENABLE_PDB=YES ^
  -D LLVM_ENABLE_LIBEDIT=NO ^
  -D LLDB_ENABLE_PYTHON=YES ^
  -D SWIFT_WINDOWS_x86_64_ICU_UC_INCLUDE="D:/Swift/Library/icu-64/usr/include" ^
  -D SWIFT_WINDOWS_x86_64_ICU_UC="D:/Swift/Library/icu-64/usr/lib/icuuc64.lib" ^
  -D SWIFT_WINDOWS_x86_64_ICU_I18N_INCLUDE="D:/Swift/Library/icu-64/usr/include" ^
  -D SWIFT_WINDOWS_x86_64_ICU_I18N="D:/Swift/Library/icu-64/usr/lib/icuin64.lib" ^
  -D CMAKE_INSTALL_PREFIX="C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr" ^
  -D PYTHON_EXECUTABLE=C:\Python27amd64\python.exe ^
  -D SWIFT_BUILD_DYNAMIC_STDLIB=YES ^
  -D SWIFT_BUILD_DYNAMIC_SDK_OVERLAY=YES ^
  -G Ninja ^
  -S D:\Swift\toolchain\llvm

其中,PYTHON_EXECUTABLE变量请按照实际情况设置为C:\Python27\python.exe或者C:\Python27amd64\python.exe。

在CMake执行完毕后,执行以下命令开始编译Swift工具链:

ninja -C D:\Swift\b\toolchain

总共需要编译5000多个C++文件,16G内存4核处理器约需2个小时。由于Ninja使用并行编译,这段时间您的电脑基本上是废了,CPU和内存均被占满,请准备好零食瓜子和iPad开始看剧(笑)。

作者在构建的时候遇到了以下几个错误:

  • MSVC warning C4819: The file contains a character that cannot be represented in the current code page (number). Save the file in Unicode format to prevent data loss.

出现这个错误基本上是因为您在编译之前没有按照上面的指示设置系统使用UTF-8页面,如果出现该提示,请立即停止编译,完成设置以后,删除之前编译好的文件并且重新开始编译。

  • Linker fatal error:LNK1102 Out of memory

如果您的内存小于等于16GB,在编译的最后阶段可能会产生这个操作。由于链接lib生成exe的时候平均每一项任务都需要6-7GB的内存,因此如果该错误出现,您需要在Ninja中设置不允许并行编译,避免多个exe一起链接导致内存耗尽:

ninja -C D:\Swift\b\toolchain -j 1

在构建完工具链之后,接着构建libdispatch模块

cmake -B D:\Swift\b\libdispatch ^
 -D CMAKE_BUILD_TYPE=RelWithDebInfo ^
 -D CMAKE_C_COMPILER=D:/Swift/b/toolchain/bin/clang-cl.exe ^
 -D CMAKE_CXX_COMPILER=D:/Swift/b/toolchain/bin/clang-cl.exe ^
 -D CMAKE_Swift_COMPILER=D:/Swift/b/toolchain/bin/swiftc.exe ^
 -D ENABLE_SWIFT=YES ^
 -G Ninja ^
 -S D:\Swift\swift-corelibs-libdispatch

使用Ninja编译:

ninja -C D:\Swift\b\libdispatch

然后是foundation模块

cmake -B D:\Swift\b\foundation -D CMAKE_BUILD_TYPE=RelWithDebInfo ^
 -D CMAKE_C_COMPILER=D:/Swift/b/toolchain/bin/clang-cl.exe ^
 -D CMAKE_Swift_COMPILER=D:/Swift/b/toolchain/bin/swiftc.exe ^
 -D CURL_LIBRARY="D:/Swift/Library/libcurl-development/usr/lib/libcurl.lib" ^
 -D CURL_INCLUDE_DIR="D:/Swift/Library/libcurl-development/usr/include" ^
 -D ICU_ROOT="D:/Swift/Library/icu-64" ^
 -D ICU_INCLUDE_DIR=D:/Swift/Library/icu-64/usr/include ^
 -D LIBXML2_LIBRARY="D:/Swift/Library/libxml2-development/usr/lib/libxml2s.lib" ^
 -D LIBXML2_INCLUDE_DIR="D:/Swift/Library/libxml2-development/usr/include/libxml2" ^
 -D ENABLE_TESTING=NO ^
 -D dispatch_DIR=D:/Swift/b/libdispatch/cmake/modules ^
 -G Ninja ^
 -S D:\Swift\swift-corelibs-foundation

编译:

ninja -C D:\Swift\b\foundation

在Foundation模块编译的时候会提示未解决的外部引用,这是一个已知问题,目前我的解决办法是在D:\Swift\b\foundation\build.ninja的Foundation.dll的构建规则中,在LINK_LIBRARIES项中手动添加D:\Swift\Library\icu-64\usr\lib\icuin64.lib D:\Swift\Library\icu-64\usr\lib\icuuc64.lib两项,这只是一个quick-and-dirty的方法。

编译完成以后添加path:

path D:\Swift\b\foundation\Foundation;%PATH%

然后是xctest模块:

cmake -B D:\Swift\b\xctest ^
 -D CMAKE_BUILD_TYPE=RelWithDebInfo ^
 -D CMAKE_Swift_COMPILER=D:/Swift/b/toolchain/bin/swiftc.exe ^
 -D dispatch_DIR=D:\Swift\b\dispatch\cmake\modules ^
 -D Foundation_DIR=D:\Swift\b\foundation\cmake\modules ^
 -D LIT_COMMAND=D:\Swift\toolchain\llvm\utils\lit\lit.py ^
 -D PYTHON_EXECUTABLE=C:\Python27amd64\python.exe ^
 -G Ninja -S D:\Swift\swift-corelibs-xctest

编译:

ninja -C D:\Swift\b\xctest

完事以后添加path:

path D:\Swift\b\xctest;%PATH%

编译llbuild:

set AR=llvm-ar
cmake -B D:\Swift\b\llbuild ^
 -D CMAKE_BUILD_TYPE=RelWithDebInfo ^
 -D CMAKE_CXX_COMPILER=cl ^
 -D CMAKE_Swift_COMPILER=D:/Swift/b/toolchain/bin/swiftc.exe ^
 -D Foundation_DIR=D:/Swift/b/foundation/cmake/modules ^
 -D dispatch_DIR=D:/Swift/b/libdispatch/cmake/modules ^
 -D SQLite3_INCLUDE_DIR=D:\Swift\Library\sqlite-3.28.0\usr\include ^
 -D SQLite3_LIBRARY=D:\Swift\Library\sqlite-3.28.0\usr\lib\sqlite3.lib ^
 -D LLBUILD_SUPPORT_BINDINGS=Swift -G Ninja -S D:\Swift\llbuild
ninja -C D:\Swift\b\llbuild

此处记得检查sqlite库的目录是否正确。

编译swift-tools-core-support:

cmake -B D:\Swift\b\tsc ^
 -D CMAKE_BUILD_TYPE=RelWithDebInfo ^
 -D CMAKE_C_COMPILER=cl ^
 -D CMAKE_Swift_COMPILER=D:/Swift/b/toolchain/bin/swiftc.exe ^
 -D Foundation_DIR=D:/Swift/b/foundation/cmake/modules ^
 -D dispatch_DIR=D:/Swift/b/libdispatch/cmake/modules ^
 -G Ninja -S D:\Swift\swift-tools-support-core
ninja -C D:\Swift\b\tsc

编译swift-package-manager:

cmake -B D:\Swift\b\spm ^
 -D CMAKE_BUILD_TYPE=RelWithDebInfo ^
 -D CMAKE_C_COMPILER=D:/Swift/b/toolchain/bin/clang-cl.exe ^
 -D CMAKE_CXX_COMPILER=D:/Swift/b/toolchain/bin/clang-cl.exe ^
 -D CMAKE_Swift_COMPILER=D:/Swift/b/toolchain/bin/swiftc.exe ^
 -D USE_VENDORED_TSC=YES ^
 -D Foundation_DIR=D:/Swift/b/foundation/cmake/modules ^
 -D dispatch_DIR=D:/Swift/b/libdispatch/cmake/modules ^
 -D LLBuild_DIR=D:/Swift/b/llbuild/cmake/modules ^
 -D TSC_DIR=D:/Swift/b/tsc/cmake/modules ^
 -G Ninja -S D:\Swift\swiftpm
ninja -C D:\Swift\b\spm

以上所有操作成功后,恭喜你,已经获得了在Windows上可用的Swift版本了。

由于经过实际测试,官方教程里的默认install方法无效,因此我们可以通过将以下路径添加到PATH中来手动安装:

D:\Swift\Library\icu-64\usr\bin
D:\Swift\Library\zlib-1.2.11\usr\bin
D:\Swift\b\toolchain\bin
D:\Swift\b\libdispatch
D:\Swift\b\llbuild\bin
D:\Swift\b\foundation\Sources\Foundation
D:\Swift\b\foundation\Sources\FoundationXML
D:\Swift\b\foundation\Sources\FoundationNetworking
D:\Swift\b\spm\bin
D:\Swift\b\tsc\bin

写一个脚本测试一下吧

随便找个位置建立一个文件,我们叫它test.swift好了。打开这个文件,输入最简单代码:

var myString = "Hello World!";
print(myString)

把D:\Swift\b\toolchain\bin添加到PATH,然后打开控制台,编译文件:

swiftc test.swift -o test.exe

swiftc稍微有点水土不服,所以必须要在输出文件中加上.exe后缀,否则不会自动添加。

在控制台里运行,就可以看到输出的Hello World!了

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/feelinghappy/article/details/109470957

智能推荐

MySQL用户管理-程序员宅基地

文章浏览阅读1.4k次,点赞31次,收藏19次。本博客主要内容介绍数据库中用户管理,包括创建用户,删除用户等,以及数据库权限方面的问题

python pde adi(抛物型差分(二维—ADI格式))_python的adi库-程序员宅基地

文章浏览阅读3k次,点赞2次,收藏12次。#coding:utf-8from mpl_toolkits.mplot3d import axes3dimport matplotlib.pyplot as pltimport numpy as npimport timedef createMatrix( m, n): A = np.zeros( (n + 2,m + 2)) Up = np.ones( (m+2,1)) * 1_python的adi库

pip 安装报错 required to install pyproject.toml-based projects_pyproject.toml-based怎么安装-程序员宅基地

文章浏览阅读9.3k次,点赞3次,收藏2次。这种情况最好不要学网上的什么下载vs,这样太麻烦了。直接去python官方网站下载whl文件。在pip指令安装,超级块。_pyproject.toml-based怎么安装

ASM空间扩容_nhdtb192-程序员宅基地

文章浏览阅读3.2k次。通过multipath -ll验证结果,并在/dev/mapper/下面能看到新加的盘[root@idc-bx-utpp-pri1 ~]# multipath -ll | grep -A6 mpath*mpathak (360060e80122bcd0050402bcd000000bf) dm-45 HITACHI,OPEN-Vsize=260G features='1 queue_i_nhdtb192

坚实原则:依赖倒置原则-程序员宅基地

文章浏览阅读139次。到目前为止,我们只研究了单一职责 , 打开/关闭 , liskov替换和接口隔离原则。 依赖倒置是我们要研究的最后一个原理之一。 该原则指出 答:高级模块不应依赖于低级模块。 两者都应依赖抽象。 B.抽象不应依赖细节。 细节应取决于抽象。 让我们开始一些违反该原理的代码。 作为软件团队,我们需要实施一个项目。 目前,软件团队包括: 后端开发人员 package com...._仓储层 依赖倒置 java

【嵌入式C语言开发】实战第017例 exit()函数_c语言exit源码-程序员宅基地

文章浏览阅读20次。由于本实例的主结构是switch循环,而在前面的实例中,已经详细地介绍过switch语句的用法,所以读者应该能够独立分析出本实例的流程。首先输入月数,如果月数是从1~12之间的整数,那么将输出月份所对应的天数。如果不是它们之间的整数,那么将执行语句exit(),直接跳出整个程序。在这个程序中,switch语句是主结构,在swtich的分支选择语句中,用到了exit0)函数。需要注意的是,如果要在程序中使用函数 exit0,则必须在程序的开头包含头文件,否则将不能够调用此函数。_c语言exit源码

随便推点

学习axios必知必会(1)~axios基本介绍、axios配置、json-server接口模拟工具_axios httpagent-程序员宅基地

文章浏览阅读5.4k次,点赞16次,收藏31次。学习axios必知必会(1)~axios基本介绍、axios配置、json-server接口模拟工具_axios httpagent

好故事-程序员宅基地

文章浏览阅读192次。我是一个老程序员,最近因为公司,因为家事,心情一直比较郁闷,偶尔翻翻,在网上看到了一个故事,内容写的就是一个毕业的学生如何打拼职场的,一开始感觉一般,但随着故事的推进,能体会到主人公的辛酸,更重要的是,发现了自己当年的影子,在这里推荐给大家,我每日发一篇,有可能会侵占版权,是吗,呵呵,但愿没有,随便吧,我想作者还应该感谢我呢。 公司杀手  一个刚毕业的大学生,对社会、对工作、对生活、对爱情充满了美好的向往,可没想到他却在跌跌撞撞中前行。他的职场之

Mybatis学习之路 第一篇_为什么dao接口和xml的包位置一致呢-程序员宅基地

文章浏览阅读143次。Mybatis学习之路 第一篇该系列教程只能帮你怎么用,并不涉及源码以及讲解。本文涉及以下内容:1. JDBC存在的问题极其代码操作2. 基于xml的Mybatis的入门程序案例3. 基于注解的Mybatis的入门程序1. JDBC存在的问题JDBC的操作非常繁琐,有很多的操作是我们开发者不需要去关注的,我们的重点应该只是SQL语句的本身。使用 preparedStatem..._为什么dao接口和xml的包位置一致呢

hmcl手机版下载_hmcl启动器正版-hmcl启动器手机版下载hmclv1.0.0-七度网-程序员宅基地

文章浏览阅读6.3k次。hmcl启动器是专为我的世界游戏设计的游戏助手工具,可快速启动以登录Minecraft,支持挂机模式和正版登录,提供游戏自动下载和资源库下载管理游戏模块,无需任何复杂的操作,设置和丰富的功能。等待您下载使用,欢迎有需要的朋友下载!hmcl启动器怎么换皮肤?首先想要换皮肤的方法跟普通的MC换皮肤方法大同小异,打开HMCL安装的根目录,找到游戏版本文件夹,并在里面选择你想要修改的游戏版本。在里面找到j..._hmcl

细说独特的APaaS软件门类-程序员宅基地

文章浏览阅读868次。文/明道云创始人任向晖美国软件企业Intuit公司在2000年前后推出了一个产品,叫做Quickbase,顾名思义,就是快速开发数据库(应用)。它开了行业先河,不仅提供低代码的企业应用开发环境,而让应用直接在一个平台上运行,用户不再需要额外编译代码和配置运行环境。当然,在那个年代,APaaS这个品类名称还没有出现,一般都统称这类应用为ASP。在接下来的十多年中,这个品类不断发展,不仅做出了Smartsheet,Outsystems, ServiceNow这些独立上市公司,像Salesforce和微软也都_apaas软件门类

单链表实现栈结构_3.3 请设计一个栈,除了常规栈支持的 pop 与 push 函数以外,还支持 min 函数,该 函-程序员宅基地

文章浏览阅读749次,点赞4次,收藏4次。最近遇到一种类型的算法题,是实现栈的pop、push、peek等方法的同时,还让实现min取得栈中最小值的功能, 题目最特殊的一点就是,要求时间复杂度为O(1)。具体的题目如下:请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。示例:MinStack minStack = new MinStack();minStack.push(-2);minStack.push(0);_3.3 请设计一个栈,除了常规栈支持的 pop 与 push 函数以外,还支持 min 函数,该 函

推荐文章

热门文章

相关标签