【译】CMake 构建工具

CMake构建工具使用教程 CMake是一个开源的、跨平台的C++构建工具。 阅读详情

CMake 是开源的跨平台的构建工具,在开发社区非常的出名,以及各种项目都有用到,本文来了解下 CMake 为何厉害。

内容来源: https://aosabook.org/en/cmake.html
原文标题:CMake
作者:Bill Hoffman && Kenneth Martin
译者:流左沙
注:翻译较原文会有一定的精简、重排和添删,主要是为了提取重要内容以达到更好的理解,请以原文为准。

In addition to a build system, over the years CMake has evolved into a family of development tools: CMake, CTest, CPack, and CDash. CMake is the build tool responsible for building software. CTest is a test driver tool, used to run regression tests. CPack is a packaging tool used to create platform-specific installers for software built with CMake. CDash is a web application for displaying testing results and performing continuous integration testing.

除了构建系统,CMake 还发展出一系列工具:CMake 是构建工具,CTest 是用于回归测试的测试工具,CPack 是用于为用 CMake 构建的软件创建指定平台安装器的打包工具,CDash 是展示测试结构和执行持续集成测试的 web 端应用。

Notion

The build tree is the directory hierarchy in which all generated files are placed. Generated files consist of the makefile, the compiled object files, and a dependency file (with a . d extension) for each source file.

构建树是放置所有生成文件的目录层级结构。生成文件包括 makefile、编译出来的目标文件、每个源文件的依赖文件。

Requirements

When CMake was being developed, the normal practice for a project was to have a configure script and Makefiles for Unix platforms, and Visual Studio project files for Windows. This duality of build systems made cross-platform development very tedious for many projects: the simple act of adding a new source file to a project was painful. The obvious goal for developers was to have a single unified build system. The developers of CMake had experience with two approaches of solving the unified build system problem.

在 CMake 被开发之时,常规的项目操作是:一个配置脚本,以及 Makefiles (Unix 平台) 或 Visual Studio 项目文件(Windows 平台)。这样的构建系统导致跨平台开发非常地难受:简单地添加一个文件到项目的操作都很痛苦。对于开发者而言,一个显然的目标就是一个统一的构建系统。

The basic constraints of the new build system would be as follows: 对于新的构建系统的限制如下

  • Depend only on a C++ compiler being installed on the system. 只依赖系统安装的 c++ 编译器
  • It must be able to generate Visual Studio IDE input files. 可以生成 VS IDE 的输入文件
  • It must be easy to create the basic build system targets, including static libraries, shared libraries, executables, and plugins. 很容易创建目标文件
  • It must be able to run build time code generators. 可以运行编译时代码生成器
  • It must support separate build trees from the source tree. 可以支持独立于源树的构建树
  • It must be able to perform system introspection, i.e., be able to determine automatically what the target system could and could not do. 反映系统问题
  • It must do dependency scanning of C/C++ header files automatically. 自动地进行 C/C++ 头文件的依赖扫描
  • All features would need to work consistently and equally well on all supported platforms. 所有特性需要协调工作,且良好运行在所有支持的平台

In order to avoid depending on any additional libraries and parsers, CMake was designed with only one major dependency, the C++ compiler (which we can safely assume we have if we're building C++ code). This did limit CMake to creating its own simple language, which is a choice that still causes some people to dislike CMake. However, at the time the most popular embedded language was Tcl. If CMake had been a Tcl-based build system, it is unlikely that it would have gained the popularity that it enjoys today.

为了避免依赖额外的库或解析器,CMake 只存在一种主要的依赖:C++ 编译器。这限制了 CMake 创建属于自己的语言,引起大家的不满。

The ability to generate IDE project files is a strong selling point for CMake, but it also limits CMake to providing only the features that the IDE can support natively. However, the benefits of providing native IDE build files outweigh the limitations. Although this decision made the development of CMake more difficult, it made the development of ITK and other projects using CMake much easier. Developers are happier and more productive when using the tools they are most familiar with. By allowing developers to use their preferred tools, projects can take best advantage of their most important resource: the developer.

可以生成 IDE 项目文件的能力是 CMake 的一大卖点,但同样也限制了 CMake 只去提供 IDE 本地支持的一些特性。但是,利大于弊。

Another early CMake requirement also came from autotools: the ability to create build trees that are separate from the source tree. This allows for multiple build types to be performed on the same source tree. It also prevents the source tree from being cluttered with build files, which often confuses version control systems.

支持创建独立于源树的构建树。这个可以使相同的源树拥有众多的构建类型,同时也防止源树被构建文件污染,导致版本控制系统的混乱。

Implementation

Environment Variables (or Not)

The trouble with this approach is that for the build to work, all of these external variables need to be set each time a build is performed. To solve this problem CMake has a cache file that stores all of the variables required for a build in one place. These are not shell or environment variables, but CMake variables. The first time CMake is run for a particular build tree, it creates a CMakeCache.txt file which stores all the persistent variables for that build. Since the file is part of the build tree, the variables will always be available to CMake during each run.

难点在于在每次编译时,所有的外部变量都需要设置一遍。为了解决这个问题,CMake 用一个 cache 文件保存所有编译所需的变量。这些不是 shell 或环境变量,只是 CMake 的变量。第一次 CMake 运行时,它会创建 CMakeCache.txt 文件来保存这些变量。这个文件就是构建树的一部分,所以对于 CMake 的每次运行都是有效的。

The Configure Step

During the configure step, CMake first reads the CMakeCache.txt if it exists from a prior run. It then reads CMakeLists.txt, found in the root of the source tree given to CMake. During the configure step, the CMakeLists.txt files are parsed by the CMake language parser. Each of the CMake commands found in the file is executed by a command pattern object. Additional CMakeLists.txt files can be parsed during this step by the include and add_subdirectory CMake commands. CMake has a C++ object for each of the commands that can be used in the CMake language. Some examples of commands are add_library, if, add_executable, add_subdirectory, and include. In effect, the entire language of CMake is implemented as calls to commands. The parser simply converts the CMake input files into command calls and lists of strings that are arguments to commands.

在配置阶段,CMake 首先读取 CMakeCache.txt 文件(如果存在的话),然后读取 CMakeLists.txt 来查找源树的根。在配置阶段,CMakeLists.txt 文件会被 CMake 语言解析器解析。每当有 CMake 命令在这个文件中被找到就会被参数模版对象执行。额外的 CMakeLists.txt 文件会在处理 include 和 add_subdirectory 命令之时解析。

The configure step essentially "runs" the user-provided CMake code. After all of the code is executed, and all cache variable values have been computed, CMake has an in-memory representation of the project to be built. This will include all of the libraries, executables, custom commands, and all other information required to create the final build files for the selected generator. At this point, the CMakeCache.txt file is saved to disk for use in future runs of CMake.

配置阶段本质上会运行用户提供的 CMake 的代码。在所有代码执行完成,以及所有变量值计算完成之后,CMake 会有一个项目的内存表示需要构建,它会包含所有的库、可执行文件、指定的命令和所有其他需要去创建最终的构建文件的信息。在此,CMakeCache.txt 文件会被保存到磁盘中,作为 CMake 之后的使用。

The Generate Step

Once the configure step has been completed, the generate step can take place. The generate step is when CMake creates the build files for the target build tool selected by the user. At this point the internal representation of targets (libraries, executables, custom targets) is converted to either an input to an IDE build tool like Visual Studio, or a set of Makefiles to be executed by make. CMake's internal representation after the configure step is as generic as possible so that as much code and data structures as possible can be shared between different built tools.

一旦配置完成,就到生成阶段了。这个阶段 CMake 会创建用户选择的目标构建工具的构建文件。在此,目标的内部表示会被转成 IDE 的构建工具,或者 make 用到的一系列 Makefiles。CMake 的内部表示需要尽可能通用,这样代码和数据结构可以被不同的构建工具所共享。

The Code

CMake is an object-oriented system using inheritance, design patterns and encapsulation

CMake 是使用了继承的面向对象的系统

The results of parsing each CMakeLists.txt file are stored in the cmMakefile object. In addition to storing the information about a directory, the cmMakefile object controls the parsing of the CMakeLists.txt file. The parsing function calls an object that uses a lex/yacc-based parser for the CMake language. Since the CMake language syntax changes very infrequently, and lex and yacc are not always available on systems where CMake is being built, the lex and yacc output files are processed and stored in the Source directory under version control with all of the other handwritten files.

解析 CMakeLists.txt 文件的结果被保存到 cmMakefile 对象当中。除了保存目录信息,它还控制文件的解析。解析函数可以使用 CMake 语言的解析器。CMake 语法不怎么变化,解析器在 CMake 构建时并非总是活跃的,解析的结果会被保存在 Source 目录下。

Another important class in CMake is cmCommand. This is the base class for the implementation of all commands in the CMake language. Each subclass not only provides the implementation for the command, but also its documentation.

另一个重要的类是 cmCommand。这是 CMake 命令的基类。每个子类不仅提供命令的实现,还有它们的文档说明。

Dependency Analysis

Since Integrated Development Environments (IDEs) support and maintain file dependency information, CMake skips this step for those build systems. For IDE builds, CMake creates a native IDE input file, and lets the IDE handle the file level dependency information. The target level dependency information is translated to the IDE's format for specifying dependency information.

由于 IDE 本身支持和持有文件依赖信息,CMake 就可以跳过这个步骤。IDE 构建时,CMake 创建一个本地的 IDE 输入文件,然后让 IDE 去处理文件级别的依赖信息。目标级别的依赖信息会被转成 IDE 的格式来指明依赖信息。

With Makefile-based builds, native make programs do not know how to automatically compute and keep dependency information up-to-date. For these builds, CMake automatically computes dependency information for C, C++ and Fortran files. Both the generation and maintenance of these dependencies are automatically done by CMake. Once a project is initially configured by CMake, users only need to run make and CMake does the rest of the work.

在使用 Makefile 构建,本地 make 程序不需要知道怎么自动计算和保存依赖信息为最新。构建时,CMake 自动的计算这些依赖信息,包括生成和保存。一旦 CMake 初始配置好了项目,用户只需要去运行 make,然后 CMake 会把剩下的工作完成。

Although users do not need to know how CMake does this work, it may be useful to look at the dependency information files for a project. This information for each target is stored in four files called depend.make, flags.make, build.make, and DependInfo.cmake. depend.make stores the dependency information for all the object files in the directory. flags.make contains the compile flags used for the source files of this target. If they change then the files will be recompiled. DependInfo.cmake is used to keep the dependency information up-to-date and contains information about what files are part of the project and what languages they are in. Finally, the rules for building the dependencies are stored in build.make. If a dependency for a target is out of date then the depend information for that target will be recomputed, keeping the dependency information current. This is done because a change to a .h file could add a new dependency.

依赖信息会被保存在 4 个文件当中,depend.make, flags.make, build.make, DependInfo.cmake 文件。depend.make 保存所有目录下的目标文件的依赖信息。flags.make 含有从源文件到目标的编译参数,如果参数被更改,这个文件也会被重新编译。DependInfo.cmake 是用来保证依赖信息是最新的,保存有哪些文件是项目的一部分以及它们是什么语言的信息。最后,构建这些依赖的规则会被保存在 build.make 文件中。如果目标的依赖过期了,那么依赖信息会被重新计算,保证依赖信息最新。这样做是因为头文件可能会添加新的依赖。

CTest and CPack

The ctest executable is used to run regression tests. A project can easily create tests for CTest to run with the add_test command. The tests can be run with CTest, which can also be used to send testing results to the CDash application for viewing on the web. CTest and CDash together are similar to the Hudson testing tool. They do differ in one major area: CTest is designed to allow a much more distributed testing environment. Clients can be setup to pull source from version control system, run tests, and send the results to CDash. With Hudson, client machines must give Hudson ssh access to the machine so tests can be run.

ctest 可执行文件用于跑回归测试。一个项目可以通过 add_test 命令很方便地创建测试。由 CTest 来执行,所以也可以把测试结果发给 CDash 来做 web 端展示。

The cpack executable is used to create installers for projects. CPack works much like the build part of CMake: it interfaces with other packaging tools. For example, on Windows the NSIS packaging tool is used to create executable installers from a project. CPack runs the install rules of a project to create the install tree, which is then given to a an installer program like NSIS. CPack also supports creating RPM, Debian .deb files, .tar, .tar.gz and self-extracting tar files.

cpack 可执行文件用来创建项目的安装器。

Reference

https://aosabook.org/en/cmake.h

CMake构建工程配置HarmonyOS编译工具链 CMake是一个跨平台的构建工具,用于管理构建过程、编译、链接和打包软件项目,它可以生成Makefile等用于不同操作系统和编译器的构建脚本。CMake的配置过程是跨平台的,因此可以在不同的操作系统上运行,例如Linux、Windows和macOS。配置(Configuration):配置阶段是CMake解析CMakeLists.txt文件的过程。在配置阶段,CMake会读取CMakeLists.txt文件,并执行其中的命令。 阅读详情

相关推荐

CMake + Ninja 构建程序示例

Ninja是一种专注于速度的小型构建系统,特别适合用于大型项目的增量编译。它最初由 Google 的工程师为加速 Chromium 浏览器的构建过程而开发,并逐渐成为一种广泛使用的替代构建工具,尤其是在配合 CMake 等高级构建配置工具时。

old_power的博客 1714

CMake构建工具

CMake 是一个跨平台的开源构建工具,用于管理和构建 C++ 项目。它允许开发人员使用简单的配置文件来描述项目的构建过程,然后根据这些配置文件生成特定于平台的构建系统(如 Makefile、Visual Studio 项目等),从而实现跨平台的项目构建。跨平台性:CMake 可以生成适用于各种操作系统和构建工具的构建文件,包括 Linux、Windows、macOS 等,以及各种编译器和 IDE。简单的配置文件。

qq_51482778的博客 2614

鸿蒙HarmonyOS NEXT开发:CMake构建工程配置HarmonyOS编译工具链

本小节介绍如何在Linux环境下,使用CMake构建工具通过ohos sdk编译cJSON三方库源码,生成ohos平台三方库的so及二进制文件。

diongdingQAQ的博客 1745

【C++】Vscode 中使用CMakeLists(推荐)构建C++项目:项目结构、各个文件夹的作用、从编码到debug

Vscode建立C++项目结构及各个文件夹的作用(不使用CMakeList.txt版)_玛丽莲茼蒿的博客-优快云博客首先要明白,vscode只是一个记事本,本身是不能够进行任何程序语言开发的。只不过这个记事本可以安装插件,可以和命令行程序结合起来使用,使得它可以进行开发。一、Vscode下C++项目的目录结构首先,类似于我们在IDEA中新建一个普通java项目,项目的目录结构是这样的:新建一个maven模板的java项目,项目的目录结构是这样的:构建一个web模板的java项目,项目的目录结构是这样的:用

qq_44886213的博客 2万+

升级构建工具,从Makefile到CMake

更多博文,请看音视频系统学习的浪漫马车之总目录 上一篇文章一篇文章入门C/C++自动构建利器之Makefile介绍了构建项目的脚本工具Makefile,相比于手动执行gcc命令来说,已经方便了太多了。不过如果只用Makefile构建项目,会有一个问题,那就是不支持跨平台的问题,因为Makefile脚本中的命令是和具体平台绑定的,比如上篇文章就是用GNU的gcc命令,那如果项目要移植到其他不能使用gcc命令的平台,那又要重新写一套Makefile,这可不符合懒惰的程序员群体,所以支持跨平台语法又更简洁的CMa

易水南风的专栏 4294

OpenCASCADE: CMake 工具构建OCCT

OpenCASCADE:编译OCCTOpenCASCADE:编译OCCT使用 CMake 工具构建启动 CMake配置流程项目生成建造 OpenCASCADE:编译OCCT 笔记 在构建 OCCT 之前,请确保已安装所有必需的第三方库。所需库的列表取决于将使用哪些 OCCT 模块以及您的偏好。典型的最小值是Freetype(可视化所必需的)和Tcl/Tk(用于 DRAW)。在 OS X 上,我们建议使用本机库。 使用 CMake 工具构建 本文介绍了基于CMake的构建过程,现在建议将其作为从源生成 Ope

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1869

cmake编译qt项目

#实际上是调用cmake安装目录\share\cmake-2.8\Modules\FindQt4.cmake #设置好一批预定义的东东 FIND_PACKAGE(Qt4 REQUIRED) # 不解释 SET(helloworld_SOURCES main.cpp hellowindow.cpp) SET(helloworld_HEADERS hellowindow.h) #处

zrjust1043的专栏 2467

CMake-GUI图形化界面管理CMake构建过程

CMake-GUI是一个基于CMake的图形用户界面,它提供了一个直观的方式来创建、配置、生成和管理项目构建系统,无需编辑CMakeLists.txt文件。对于IT行业的专业人员,特别是那些负责项目构建和集成的开发人员,CMake-GUI带来了诸多优势。CMakeLists.txt是CMake的配置脚本文件,它包含了项目构建的所有指令。它指导CMake如何处理源代码,生成适合特定平台的构建系统。每个CMake项目都至少需要一个CMakeLists.txt文件。

weixin_42591413的博客 2496

【C++开发】CMake构建工具

CMake工具的使用

m0_74246469的博客 1434

【Konva】解决图形加入group后无法获取到绝对坐标,以及group内绘制新图形坐标偏移的问题

解决konva绘图开发时“图形加入group后坐标不会变动,无法获取到绝对坐标”的问题

近10年前端开发工作经验,为你分享日常经验所得 1499

第二章-Ethernet/IP基本概念

在I/O连接中,一旦连接建立,就不会有请求-响应数据,只会按照连接建立时指定的生产触发器所确定的时间间隔生产和消耗带有ConnectionID的数据。连接是通过连接管理器对象的Forward请求服务建立的,ForwardOpen请求包含所有连接参数,包括传输类、生产触发器、定时信息、电子密钥和连接ID。I/O适配器:I/O适配器从I/O扫描器接收隐式通信连接请求,然后以请求的速率生成I/O数据。I/O扫描器:启动与I/O适配器的隐式通信,需要处理连接的配置以及如何配置适配器。扫描器还支持启动显式报文。

_Nooice的博客 1233

cmake跨平台构建工具

cmake跨平台构建工具

wtl666_6的博客 7354

ABAP调用启信宝API

方法参数定义如下 源代码如下 method qxb_http_get. data:lv_url type string. "===============初始化=============================================== "获取http实例 cl_http_client=>create_by_destination( exporting destination = space importing

u012232542的博客 1311

【CMake】CMake构建项目入门

本文介绍了CMake跨平台构建工具的基本使用方法。首先讲解了CMake的安装(Ubuntu系统)和版本检查,然后通过Hello World示例演示了完整的构建流程:创建CMakeLists.txt配置文件,使用PROJECT、SET、MESSAGE、ADD_EXECUTABLE等核心指令,以及内部/外部构建方式的区别。重点解析了CMake语法规则,包括变量引用、指令参数格式等注意事项。最后建议完善工程结构,添加src源代码目录、doc文档目录和README等文件。文章配有详

Antonio915的博客 1694

Depth Anything 3实战:如何用DINOv2 Transformer一键生成3D高斯点云?

本文详细介绍了Depth Anything 3(DA3)模型如何利用DINOv2 Transformer架构,实现从单张或多视角图像一键生成高质量3D高斯点云。DA3通过统一的深度-射线表示和自适应注意力机制,简化了传统复杂的3D重建流程,并可直接微调用于3D高斯渲染,为图形学、CV和内容创作领域提供了高效的端到端解决方案。

postgres8guard的博客 722

cmake:命令行工具cmake

概要 Generate a Project Buildsystem cmake [<options>] <path-to-source> cmake [<options>] <path-to-existing-build> cmake [<options>] -S <path-to-source> -B <path-to-build> Build a Project cmake --build <dir&gt

OceanStar的博客 1万+

《嵌入式最全面试题-Offer直通车》目录

嵌入式领域蓬勃发展,嵌入式工程师成为众多科技企业争抢的热门人才。如果你渴望在嵌入式面试中脱颖而出,成功斩获心仪的 offer,那么《嵌入式最全面试题 - Offer直通车》将是你的绝佳选择。本专栏是优快云是热门专栏,100多篇文章,总字数过百万。每月新增订阅用户过百,现在是优惠价9.9元,后面要逐渐恢复原价,早订阅早受益。本专栏精心整理了众多超万字的精华内容,总计达数十万字的嵌入式面试题总结。涵盖从嵌入式基础知识到各大厂面试真题,从驱动开发到操作系统,从硬件到软件等各个方面。

大模型大数据攻城狮的专栏 8392

【CMake】CMake工程构建全流程 以及 相关命令行工具的介绍

本文介绍了CMake构建工具的优势、安装配置方法及工程构建流程。CMake相比传统Makefile方式具有跨平台、语法简单、自动包管理和IDE支持度高等优势。文章详细说明了在Ubuntu系统中安装CMake和VS Code相关插件的步骤,并通过示例项目展示了CMakeLists.txt的基本配置方法。最后阐述了CMake的标准构建流程,包括生成构建系统、编译、测试、安装和打包等步骤,重点解释了源内构建和源外构建的区别。CMake已成为C/C++项目构建的事实标准,能有效提升开发效率。

无限进步!!! 1047

华为OD机试真题 新系统2026-04-01 C语言 实现【空间占用计算】

员工A的磁盘空间经常被耗尽,他需要找到占用空间最大的目录或文件,然后决定如何清理文件释放空间。给定某一目录,请编写程序帮助他统计该目录内一级子目录和文件的占用空间,并返回目标目录一级子项(文件或子目录)中占用空间最大的项。

MISAYAONE的博客 229

【项目实战】Java9的新特性模块化(Module)

Java 9引入了模块化系统,称为"Java Platform Module System"(JPMS)这个系统允许将Java程序分成模块,每个模块都有自己的规范,可以明确地声明它依赖于哪些其他模块,并且只能访问被其所依赖的模块暴露的API。这样可以模块化就是增加了更高级别的聚合,是Package的封装体。Package是一些类路径名字的约定,而模块是一个或多个Package组成的封装体。

奶爸的编程之路,也就一周冷个三天 1896

RT-Thread SCons构建工具 全面详解 对比cmake 强过Makefile

笔者首先对于scons这个工具的引入是在学习cmake的时候有所听闻,并在真正学习RT架构的时候由于RTthread的迭代之后就不用Make/Makefile ,RT-Thread 早期使用 Make/Makefile 构建。从 RT-Thread 0.3.0 开始,RT-Thread 开发团队逐渐引入了 SCons 构建系统,引入 SCons 唯一的目是:使大家从复杂的 Makefile 配置、IDE 配置中脱离出来,把精力集中在 RT-Thread 功能开发上。

你要拥有一个你喜欢并且热爱的人生 1万+

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

CMake构建

文章目录CMake是用来干啥的?Ubuntu安装CMake一、CMake构建的简单使用:Helloworld二、CMake构建:多源文件初级:少数源文件 add_executable()进阶:更多源文件 aux_source_directory() 或 set()三、CMake构建:多目录多源文件新命令:include_directories()四、CMake构建:正规组织结构方式一:两个CMakeLists.txt方式二:一个CMakeLists.txt五、CMake构建:编译动态库和静态库六、CMake

似水流年 2883

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

CMake、Make、Tup 以及其他构建工具对比

CMake,CMakeLists.txt,Make,Makefile,tup,Tupfile,Ninja,build.ninja,Bazel,BUILD,Meson,meson.build

qq_18677445的博客 1488

CMake 与 Make:构建工具的差异与选择

CMake 和 Make:项目构建工具用法详细

深耕嵌入式领用多年, 致力于分享嵌入式领域技术! 1913

【cmake工具构建工程】

include_directories 命令用来指明头文件所在的路径,并且使用到了 PROJECT_SOURCE_DIR 变量,该变量指向了一个路径,我们已经加入了多个源文件,但是这些源文件都是放在同一个目录下,这样还是不太正规,我们应该将这些源文件按照类型、功能、模块给它们放置到不同的目录下。add_subdirectory 命令, 该命令告诉 cmake 去子目录中寻找新的CMakeLists.txt 文件并解析它;⚫ libhello 目录下的 CMakeLists.txt。

sinat_23896491的博客 691
上一篇: 【译】Ninja 构建系统
下一篇: 【译】LLVM 的设计
流左沙
博客等级 码龄11年 46粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值