CMAKE的使用

原文链接:

http://www.cnblogs.com/lidabo/p/3974305.html

https://my.oschina.net/u/1046919/blog/477645


一、      基本使用

安装:下载二进制包后可直接解压使用

从源码安装则执行命令:./bootstrap; make; make install——尝试执行bootstrap失败

使用:cmake dir_path,生成工程文件或makefile文件

二、      概念

out-of-source build,与in-source build相对,即将编译输出文件与源文件放到不同目录中;

三、      基本结构

1,依赖CMakeLists.txt文件,项目主目标一个,主目录中可指定包含的子目录;

2,在项目CMakeLists.txt中使用project指定项目名称,add_subdirectory添加子目录

3,子目录CMakeLists.txt将从父目录CMakeLists.txt继承设置(TBD,待检验)

四、      语法

1.       #注释

2.       变量:使用set命令显式定义及赋值,在非if语句中,使用${}引用,if中直接使用变量名引用;后续的set命令会清理变量原来的值;

3.       command (args ...)  #命令不分大小写,参数使用空格分隔,使用双引号引起参数中空格

4.       set(var a;b;c) <=> set(var a b c)  #定义变量var并赋值为a;b;c这样一个string list

5.       Add_executable(${var}) <=> Add_executable(a b c)   #变量使用${xxx}引用

6.       条件语句:

if(var) #var 非empty 0 N No OFF FALSE... #非运算使用NOT

       …

else()/elseif() … endif(var)

7.       循环语句

Set(VAR a b c)

Foreach(f ${VAR})       …Endforeach(f)

8.       循环语句

WHILE() … ENDWHILE()

五、      内部变量

CMAKE_C_COMPILER:指定C编译器

CMAKE_CXX_COMPILER

CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项

EXECUTABLE_OUTPUT_PATH:可执行文件的存放路径

LIBRARY_OUTPUT_PATH:库文件路径

CMAKE_BUILD_TYPE::build 类型(Debug, Release, ...),CMAKE_BUILD_TYPE=Debug

BUILD_SHARED_LIBS:Switch between shared and static libraries

内置变量的使用:

>> 在CMakeLists.txt中指定,使用set

>> cmake命令中使用,如cmake -DBUILD_SHARED_LIBS=OFF

六、      命令

project (HELLO)   #指定项目名称,生成的VC项目的名称;

>>使用${HELLO_SOURCE_DIR}表示项目根目录

include_directories:指定头文件的搜索路径,相当于指定gcc的-I参数

>> include_directories (${HELLO_SOURCE_DIR}/Hello)  #增加Hello为include目录

link_directories:动态链接库或静态链接库的搜索路径,相当于gcc的-L参数

       >> link_directories (${HELLO_BINARY_DIR}/Hello)     #增加Hello为link目录

add_subdirectory:包含子目录

       >> add_subdirectory (Hello)

add_executable:编译可执行程序,指定编译,好像也可以添加.o文件

       >> add_executable (helloDemo demo.cxx demo_b.cxx)   #将cxx编译成可执行文件——

add_definitions:添加编译参数

>> add_definitions(-DDEBUG)将在gcc命令行添加DEBUG宏定义;

>> add_definitions( “-Wall -ansi –pedantic –g”)

target_link_libraries:添加链接库,相同于指定-l参数

>> target_link_libraries(demo Hello) #将可执行文件与Hello连接成最终文件demo

add_library:

>> add_library(Hello hello.cxx)  #将hello.cxx编译成静态库如libHello.a

add_custom_target:

message( status|fatal_error, “message”):

set_target_properties( ... ): lots of properties... OUTPUT_NAME, VERSION, ....

link_libraries( lib1 lib2 ...): All targets link with the same set of libs

 

project 命令

    语句 : project(<projectname> [languageName1 languageName2 … ] )

    作用 : 指定项目名


cmake_minimum_required 命令

    语句 : cmake_minimum_required(VERSION major[.minor[.patch[.tweak]]] [FATAL_ERROR])

    作用 : 指定cmake需要的最小版本

    

aux_source_directory 命令

    语句 : aux_source_directory(<dir> <variable>)

    作用 : 获取指定目录下的所有文件,保存到variable中,包括 .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx文件

    示例 : aux_source_directory(. var)#获取当前目录中源文件


add_executable 命令

    语句 : add_executable(<name> [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] source1 source2 … sourceN)

    作用 : 将指定文件source编译成可执行文件,命名位name

    示例 : add_executable(hello hello.cpp)


add_library 命令

    语句 : add_library([STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 source2 … sourceN)

    作用 : 添加一个名为<name>的库文件,指定STATIC,SHARED,或者MODULE参数用来指定要创建的库的类型。STATIC库是目标文件的归档文件,在链接其它目标的时候使用。SHARED库会被动态链接,在运行时被加载。MODULE库是不会被链接到其它目标中的插件,但是可能会在运行时使用dlopen-系列的函数动态链接。如果没有类型被显式指定,这个选项将会根据变量BUILD_SHARED_LIBS的当前值是否为真决定是STATIC还是SHARED

    示例 : add_library(Lib ${DIR_SRCS})


add_dependencies 命令

    语句 : add_dependencies(target-name depend-target1 depend-target2 …)

    作用 : 用于指定某个目标(可执行文件或者库文件)依赖于其他的目标。这里的目标必须是 add_executable、add_library、add_custom_target 命令创建的目标


add_subdirectory 命令

    语句 : add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

    作用 : 用于添加一个需要进行构建的子目录

    示例 : add_subdirectory(directory)


target_link_libraries 命令

    语句 : target_link_libraries(<target> [item1 [item2 […]]] [[debug|optimized|general] ] …)

    作用 : 用于指定 target 需要链接 item1 item2 …。这里 target 必须已经被创建,链接的 item 可以是已经存在的 target(依赖关系会自动添加)

    示例 : target_link_libraries(Main Lib)


set 命令

    语句 : set(<variable> <value> [[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE])

    作用 : 用于设定变量 variable 的值为 value。variable可以自己定义

    示例 : set(var "${list}_exe")


unset 命令

    语句 : unset(<variable> [CACHE])

    作用 : 用于移除变量 variable。如果指定了 CACHE 变量将被从 Cache 中移除。

    示例 : unset(VAR CACHE)


message 命令

    语句 : message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] “message to display” …)

    作用 : 输出信息

    示例 : message("hello world")


include_directories 命令

    语句 : include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)

    作用 : 用于设定目录,这些设定的目录将被编译器用来查找 include 文件

    示例 : include_directories(${PROJECT_SOURCE_DIR}/lib)


find_path 命令

    语句 : find_path(<VAR> name1 [path1 path2 …])

    作用 : 用于查找包含文件 name1 的路径,如果找到则将路径保存在 VAR 中(此路径为一个绝对路径),如果没有找到则结果为 <VAR>-NOTFOUND。默认的情况下,VAR 会被保存在 Cache 中,这时候我们需要清除 VAR 才可以进行下一次查询(使用 unset 命令)


add_definitions 命令

    语句 : find_library(<VAR> name1 [path1 path2 …])

    作用 : 用于添加编译器命令行标志(选项),通常的情况下我们使用其来添加预处理器定义

    示例 : add_definitions(-D_UNICODE -DUNICODE)

execute_process 命令

    语句 : execute_process(COMMAND <cmd1> [args1...]]  [COMMAND <cmd2> [args2...] [...]] 

                                            [WORKING_DIRECTORY <directory>]

                                            [TIMEOUT <seconds>]

                                            [RESULT_VARIABLE <variable>]  

                                            [OUTPUT_VARIABLE <variable>]  

                                            [ERROR_VARIABLE <variable>]  

                                            [INPUT_FILE <file>]

                                            [OUTPUT_FILE <file>] 

                                            [ERROR_FILE <file>]  

                                            [OUTPUT_QUIET]  

                                            [ERROR_QUIET 

                                            [OUTPUT_STRIP_TRAILING_WHITESPACE]

                                            [ERROR_STRIP_TRAILING_WHITESPACE])


    作用 : 用于执行一个或者多个外部命令。每一个命令的标准输出通过管道转为下一个命令的标准输入。WORKING_DIRECTORY 用于指定外部命令的工作目录,RESULT_VARIABLE 用于指定一个变量保存外部命令执行的结果,这个结果可能是最后一个执行的外部命令的退出码或者是一个描述错误条件的字符串,OUTPUT_VARIABLE 或者 ERROR_VARIABLE 用于指定一个变量保存标准输出或者标准错误,OUTPUT_QUIET 或者 ERROR_QUIET 用于忽略标准输出和标准错误。

    示例 : execute_process(COMMAND ls)


file 命令

    语句 : file(WRITE filename "message to write"... ) 

    作用 : WRITE选项将会写一条消息到名为filename的文件中。如果文件已经存在,该命令会覆盖已有的文件;如果文件不存在,它将创建该文件。


    ===========================================================


    语句 : file(APPEND filename "message to write"... )

    作用 : APPEND选项和WRITE选项一样,将会写一条消息到名为filename的文件中,只是该消息会附加到文件末尾。 


    ===========================================================


    语句 : file(READ filename variable [LIMIT numBytes] [OFFSET offset] [HEX]) 

    作用 : READ选项将会读一个文件中的内容并将其存储在变量里。读文件的位置从offset开始,最多读numBytes个字节。如果指定了HEX参数,二进制代码将会转换为十六进制表达方式,并存储在变量里。 

    

    ===========================================================


    语句 : file(STRINGS filename variable [LIMIT_COUNT num] [LIMIT_INPUT numBytes]  [LIMIT_OUTPUT numBytes] 

                                                           [LENGTH_MINIMUM numBytes]   [LENGTH_MAXIMUM numBytes]

                                                            [NEWLINE_CONSUME] [REGEX regex]  [NO_HEX_CONVERSION]) 

    作用 : STRINGS将会从一个文件中将一个ASCII字符串的list解析出来,然后存储在variable变量中。文件中的二进制数据会被忽略。回车换行符会被忽略。它也可以用在Intel的Hex和Motorola的S-记录文件;读取它们时,它们会被自动转换为二进制格式。可以使用NO_HEX_CONVERSION选项禁止这项功能。LIMIT_COUNT选项设定了返回的字符串的最大数量。LIMIT_INPUT设置了从输入文件中读取的最大字节数。LIMIT_OUTPUT设置了在输出变量中存储的最大字节数。LENGTH_MINIMUM设置了要返回的字符串的最小长度;小于该长度的字符串会被忽略。LENGTH_MAXIMUM设置了返回字符串的最大长度;更长的字符串会被分割成不长于最大长度的字符串。NEWLINE_CONSUME选项允许新行被包含到字符串中,而不是终止它们。REGEX选项指定了一个待返回的字符串必须满足的正则表达式。 


    ===========================================================


    语句 : file(GLOB variable [RELATIVE path] [globbing expressions]...) 

    作用 : GLOB选项将会为所有匹配查询表达式的文件生成一个文件list,并将该list存储进变量variable里。文件名查询表达式与正则表达式类似,只不过更加简单。如果为一个表达式指定了RELATIVE标志,返回的结果将会是相对于给定路径的相对路径。 


    ===========================================================


     语句 : file(GLOB_RECURSE variable [RELATIVE path] [FOLLOW_SYMLINKS] [globbing expressions]...) 

    作用 : GLOB_RECURSE选项将会生成一个类似于通常的GLOB选项的list,只是它会寻访所有那些匹配目录的子路径并同时匹配查询表达式的文件。作为符号链接的子路径只有在给定FOLLOW_SYMLINKS选项或者cmake策略CMP0009被设置为NEW时,才会被寻访到。参见cmake --help-policy CMP0009 查询跟多有用的信息。 


    ===========================================================


    语句 : file(RENAME <oldname> <newname>) 

    作用 : RENAME选项对同一个文件系统下的一个文件或目录重命名。

 

    ===========================================================


    语句 : file(REMOVE [file1 ...]) 

    作用 : REMOVE选项将会删除指定的文件,包括在子路径下的文件。

    ===========================================================


    语句 : file(REMOVE_RECURSE [file1 ...]) 

    作用 : REMOVE_RECURSE选项会删除给定的文件以及目录,包括非空目录。 


    ===========================================================


    语句 : file(MAKE_DIRECTORY [directory1 directory2 ...]) 

    作用 : MAKE_DIRECTORY选项将会创建指定的目录,如果它们的父目录不存在时,同样也会创建。(类似于mkdir命令——译注) 


    ===========================================================


    语句 : file(RELATIVE_PATH variable directory file) 

    作用 : RELATIVE_PATH选项会确定从direcroty参数到指定文件的相对路径。 


    ===========================================================


    语句 : file(TO_CMAKE_PATH path result) 

    作用 : TO_CMAKE_PATH选项会把path转换为一个以unix的 / 开头的cmake风格的路径。输入可以是一个单一的路径,也可以是一个系统路径,比如"$ENV{PATH}"。注意,在调用TO_CMAKE_PATH的ENV周围的双引号只能有一个参数(Note the double quotes around the ENV call TO_CMAKE_PATH only takes one argument. 原文如此。 


    ===========================================================


    语句 : file(TO_NATIVE_PATH path result) 

    作用 : TO_NATIVE_PATH选项与TO_CMAKE_PATH选项很相似,但是它会把cmake风格的路径转换为本地路径风格:windows下用\,而unix下用/。 


    ===========================================================


    语句 : file(DOWNLOAD url file [TIMEOUT timeout] [STATUS status] [LOG log] [EXPECTED_MD5 sum] [SHOW_PROGRESS]) 

    作用 : DOWNLOAD 将给定的URL下载到指定的文件中。如果指定了LOG var选项,下载日志将会被输出到var中。如果指定了STATUS var选项,下载操作的状态会被输出到var中。该状态返回值是一个长度为2的list。list的第一个元素是操作的数字返回值,第二个返回值是错误的字符串值。错误信息如果是数字0,操作中没有发生错误。如果指定了TIMEOUT time选项,在time秒之后,操作会超时退出;time应该是整数。如果指定了EXPECTED_MD5 sum选项,下载操作会认证下载的文件的实际MD5和是否与期望值匹配。如果不匹配,操作将返回一个错误。如果指定了SHOW_PROGRESS选项,进度信息会以状态信息的形式被打印出来,直到操作完成。


cmake 常用语句

    

条件控制

======================  

      if(expression)

        # ...

        elseif(expression2)

            # ...

        else()

            # ...

        endif()

======================

    对于 if(string) 来说:

  1. 如果 string 为(不区分大小写)1、ON、YES、TRUE、Y、非 0 的数则表示真

  2. 如果 string 为(不区分大小写)0、OFF、NO、FALSE、N、IGNORE、空字符串、以 -NOTFOUND 结尾的字符串则表示假

  3. 如果 string 不符合上面两种情况,则 string 被认为是一个变量的名字。变量的值为第二条所述的各值则表示假,否则表示真


===========================================================

    if中的语句:

  1. if(NOT expression)
    为真的前提是 expression 为假

  2. if(expr1 AND expr2)
    为真的前提是 expr1 和 expr2 都为真

  3. if(expr1 OR expr2)
    为真的前提是 expr1 或者 expr2 为真

  4. if(COMMAND command-name)
    为真的前提是存在 command-name 命令、宏或函数且能够被调用

  5. if(EXISTS name)
    为真的前提是存在 name 的文件或者目录(应该使用绝对路径)

  6. if(file1 IS_NEWER_THAN file2)
    为真的前提是 file1 比 file2 新或者 file1、file2 中有一个文件不存在(应该使用绝对路径)

  7. if(IS_DIRECTORY directory-name)
    为真的前提是 directory-name 表示的是一个目录(应该使用绝对路径)

  8. if(variable|string MATCHES regex)
    为真的前提是变量值或者字符串匹配 regex 正则表达式

  9. if(variable|string LESS variable|string)
    if(variable|string GREATER variable|string)
    if(variable|string EQUAL variable|string)
    为真的前提是变量值或者字符串为有效的数字且满足小于(大于、等于)的条件

  10. if(variable|string STRLESS variable|string)
    if(variable|string STRGREATER variable|string)
    if(variable|string STREQUAL variable|string)
    为真的前提是变量值或者字符串以字典序满足小于(大于、等于)的条件

  11. if(DEFINED variable)
    为真的前提是 variable 表示的变量被定义了


循环结构

    foreach循环

======================

        set(VAR a b c)

        foreach(f ${VAR})

            message(${f})

        endforeach()

====================== 

    


    while循环

======================

        set(VAR 5)

        while(${VAR} GREATER 0)

            message(${VAR})

            math(EXPR VAR "${VAR} - 1")

        endwhile()

======================



宏定义

    macro循环

======================

        # 定义一个宏 hello

        macro(hello MESSAGE)

            message(${MESSAGE})

        endmacro()

        # 调用宏 hello

        hello("hello world")

        # 定义一个函数 hello

        function(hello MESSAGE)

            message(${MESSAGE})

        endfunction()

======================


函数定义

    

======================

        function(get_func RESULT)

            # RESULT 的值为实参的值,因此需要使用 ${RESULT}

            # 这里使用 PARENT_SCOPE 是因为函数会构建一个局部作用域

            set(${RESULT} "Hello Function" PARENT_SCOPE)

        endfunction()

         

        macro(get_macro RESULT)

            set(${RESULT} "Hello Macro")

        endmacro()

         

        get_func(V1)

        # 输出 Hello Function

        message(${V1})

         

        get_macro(V2)

        # 输出 Hello Macro

        message(${V2})


======================


字符串控制

    string(REGEX MATCH (regular_expression) (output variable) (input) [(input)...])
    string(REGEX MATCHALL (regular_expression) (output variable) (input) [(input)...])
    string(REGEX REPLACE (regular_expression) (replace_expression) (output variable) (input) [(input)...])
    string(REPLACE (match_string) (replace_string) (output variable) (input) [(input)...])
    string(COMPARE EQUAL (string1) (string2) (output variable))
    string(COMPARE NOTEQUAL (string1) (string2) (output variable))
    string(COMPARE LESS (string1) (string2) (output variable))
    string(COMPARE GREATER (string1) (string2) (output variable))
    string(ASCII (number) [(number) ...] (output variable))
    string(CONFIGURE (string1) (output variable) [@ONLY] [ESCAPE_QUOTES])
    string(TOUPPER (string1) (output variable))
    string(TOLOWER (string1) (output variable))
    string(LENGTH (string) (output variable))
    string(SUBSTRING (string) (begin) (length) (output variable))
    string(STRIP (string) (output variable))
    string(RANDOM [LENGTH (length)] [ALPHABET (alphabet)] (output variable))




cmake 常用变量


  1. UNIX 如果为真,表示为 UNIX-like 的系统,包括 Apple OS X 和 CygWin

  2. WIN32 如果为真,表示为 Windows 系统,包括 CygWin

  3. APPLE 如果为真,表示为 Apple 系统

  4. CMAKE_SIZEOF_VOID_P 表示 void* 的大小(例如为 4 或者 8),可以使用其来判断当前构建为 32 位还是 64 位

  5. CMAKE_CURRENT_LIST_DIR 表示正在处理的 CMakeLists.txt 文件的所在的目录的绝对路径(2.8.3 以及以后版本才支持)

  6. CMAKE_ARCHIVE_OUTPUT_DIRECTORY 用于设置 ARCHIVE 目标的输出路径

  7. CMAKE_LIBRARY_OUTPUT_DIRECTORY 用于设置 LIBRARY 目标的输出路径

  8. CMAKE_RUNTIME_OUTPUT_DIRECTORY 用于设置 RUNTIME 目标的输出路径



七、      说明

1,CMAKE生成的makefile能够处理好.h文件更改时只编译需要的cpp文件;

八、      FAQ

1)  怎样获得一个目录下的所有源文件

>> aux_source_directory(<dir> <variable>)

>> 将dir中所有源文件(不包括头文件)保存到变量variable中,然后可以add_executable (ss7gw ${variable})这样使用。

2)  怎样指定项目编译目标

>>  project命令指定

3)  怎样添加动态库和静态库

>> target_link_libraries命令添加即可

4)  怎样在执行CMAKE时打印消息

>> message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display" ...)

>> 注意大小写

5)  怎样指定头文件与库文件路径

>> include_directories与link_directories

>>可以多次调用以设置多个路径

>> link_directories仅对其后面的targets起作用

6)  怎样区分debug、release版本

>>建立debug/release两目录,分别在其中执行cmake -DCMAKE_BUILD_TYPE=Debug(或Release),需要编译不同版本时进入不同目录执行make即可;

Debug版会使用参数-g;Release版使用-O3 –DNDEBUG

>> 另一种设置方法——例如DEBUG版设置编译参数DDEBUG

IF(DEBUG_mode)

    add_definitions(-DDEBUG)

ENDIF()

在执行cmake时增加参数即可,例如cmake -D DEBUG_mode=ON

7)  怎样设置条件编译

例如debug版设置编译选项DEBUG,并且更改不应改变CMakelist.txt

>> 使用option command,eg:

option(DEBUG_mode "ON for debug or OFF for release" ON)

IF(DEBUG_mode)

    add_definitions(-DDEBUG)

ENDIF()

>> 使其生效的方法:首先cmake生成makefile,然后make edit_cache编辑编译选项;Linux下会打开一个文本框,可以更改,该完后再make生成目标文件——emacs不支持make edit_cache;

>> 局限:这种方法不能直接设置生成的makefile,而是必须使用命令在make前设置参数;对于debug、release版本,相当于需要两个目录,分别先cmake一次,然后分别make edit_cache一次;

>> 期望的效果:在执行cmake时直接通过参数指定一个开关项,生成相应的makefile——可以这样做,例如cmake –DDEBUGVERSION=ON

8)  怎样添加编译宏定义

>> 使用add_definitions命令,见命令部分说明

9)  怎样添加编译依赖项

用于确保编译目标项目前依赖项必须先构建好

>>add_dependencies

10)        怎样指定目标文件目录

>> 建立一个新的目录,在该目录中执行cmake生成Makefile文件,这样编译结果会保存在该目录——类似

>> SET_TARGET_PROPERTIES(ss7gw PROPERTIES

                      RUNTIME_OUTPUT_DIRECTORY "${BIN_DIR}")

11)        很多文件夹,难道需要把每个文件夹编译成一个库文件?

>> 可以不在子目录中使用CMakeList.txt,直接在上层目录中指定子目录

12)        怎样设定依赖的cmake版本

>>cmake_minimum_required(VERSION 2.6)

13)        相对路径怎么指定

>> ${projectname_SOURCE_DIR}表示根源文件目录,${ projectname _BINARY_DIR}表示根二进制文件目录?

14)        怎样设置编译中间文件的目录

>> TBD

15)        怎样在IF语句中使用字串或数字比较

>>数字比较LESS、GREATER、EQUAL,字串比STRLESS、STRGREATER、STREQUAL,

>> Eg:

set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)

set(AAA abc)

IF(AAA STREQUAL abc)

    message(STATUS "true")   #应该打印true

ENDIF()

16)        更改h文件时是否只编译必须的cpp文件

>> 是

17)        机器上安装了VC7和VC8,CMAKE会自动搜索编译器,但是怎样指定某个版本?

>> TBD

18)        怎样根据OS指定编译选项

>> IF( APPLE ); IF( UNIX ); IF( WIN32 )

19)        能否自动执行某些编译前、后命令?

>> 可以,TBD

20)        怎样打印make的输出

make VERBOSE=1

参考文献:

[1] CMake_Tutorial.pdf

[2] CMake使用总结,http://blog.csdn.net/keensword007/archive/2008/07/16/2663235.aspx

[3] http://www.cmake.org/

[4] 安装包中文档

[5] Andrej Cedilnik,HOWTO: Cross-Platform Software Development Using CMake,October, 2003

[6] Cjacker,CMake实践.PDF



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值