cmake相关知识

gcc main.c # 生成a.out
直接执行: ./a.out
gcc main.c -o main  # 生成main
直接执行: ./main

不同平台(GNU make、QT的qmake、微软的MS nmake、pamke、makeapp等)makefile格式不一样,cmake无关平台。

# 写cmake文件:CMakeLists.txt
# 1、单目录单文件,main.cpp
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
project( demo1 )
add_executable( demo1 main.cpp )

# 2、单目录多文件,多了myfunc.cpp和myfunc.hpp
-方法 1
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
project( demo2 )
add_executable( demo2 main.cpp myfunc.cpp )

# 发现一个目录下所有的源代码文件并将列表存储在一个变量中
# 把当前目录下的所有源码文件名赋给变量DIR_HELLO_SRCS
AUX_SOURCE_DIRECTORY(dir VAR)

- 方法2
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
project( demo2 )
AUX_SOURCE_DIRECTORY(./ DIR_SRCS)
add_executable( demo1 ${DIR_SRCS} )

# 3、多目录多文件
tree显示当前目录结构
- CMakeLists.txt
- demo.cpp
- mylib
	- CMakeLists.txt
	- mymath.cpp
	- mymath.hpp

- CMakeLists.txt
./mylib/CMakeLists.txt
AUX_SOURCE_DIRECTORY(./ DIR_LIB_SRCS)
add_library( Mylib ${DIR_LIB_SRCS} )

./CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
project( demo3 )
add_subdirectory(./mylib)  # 增加静态库
AUX_SOURCE_DIRECTORY(./ DIR_SRCS)
add_executable( demo3 ${DIR_SRCS} )
target_link_libraries(demo3 Mylib)  # 链接静态库

# 4、多目录多文件标志工程
目录树
- CMakeLists.txt
- build  # 最后在build文件夹编译。cmake ..
- src
	- CMakeLists.txt
	- demo.cpp
- mylib
	- CMakeLists.txt
	- mymath.cpp
	- mymath.hpp

./src/CMakeLists.txt
include_directories(${PROJECT_SOURCE_DIR}/mylib)
# project_binary_dir 为cmake所在目录,build
set(executable_output_path ${project_binary_dir}/bin)  # 设置生成文件的输出目录
AUX_SOURCE_DIRECTORY(./ DIR_SRCS)
add_executable( demo4 ${DIR_SRCS} )
target_link_libraries(demo4 Mylib)

./mylib/CMakeLists.txt
AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
set(library_output_path ${project_binary_dir}/lib)  # 设置库文件的输出目录
add_library( Mylib ${DIR_LIB_SRCS} )

./CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
project( demo4 )
add_subdirectory(./mylib)  # 增加子目录
add_subdirectory(./src) 

# 5、自定义编译选项
目录树
- CMakeLists.txt
- build  # 最后在build文件夹编译。cmake ..
- config
    - config.hpp.in
- src
	- CMakeLists.txt
	- demo.cpp
- mylib
	 - CMakeLists.txt
	- mymath.cpp
	- mymath.hpp

./config/config.hpp.in
#cmakedefine USE_MYMATH  # 定义一个宏,是否使用mylib或者标准库的math
    
./src/CMakeLists.txt
# project_binary_dir 为cmake所在目录,build
set(executable_output_path ${project_binary_dir}/bin)  # 设置生成文件的输出目录
configure_file(
	"${PROJECT_SOURCE_DIR}/config/config.hpp.in"  # 输入
    "${PROJECT_SOURCE_DIR}/config/config.hpp"  # 输出
)
option(USE_MYMATH ON)  # 决定变量的值
if(USE_MYMATH)
include_directories(${PROJECT_SOURCE_DIR}/mylib)
endif(USE_MYMATH)
AUX_SOURCE_DIRECTORY(./ DIR_SRCS)
add_executable( demo5 ${DIR_SRCS} )
target_link_libraries(demo5 Mylib)
    
./mylib/CMakeLists.txt
AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)  
set(library_output_path ${project_binary_dir}/lib)  # 设置库文件的输出目录
add_library( Mylib ${DIR_LIB_SRCS} )

./CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
project( demo4 )
add_subdirectory(./mylib)  # 增加子目录
add_subdirectory(./src) 
    
./src/demp.cpp
#ifdef  
    #include "../mylib/mymath.hpp"
#else
    #include <math.h>
#endif
    
ccmake 查看
    
# 6、VS下调试
./CMakeLists.txt   
增加:
add_definitions(-std=C++11)
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -00 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -03 -Wall")
    
camke,make后。Run -> Start Debugging -> 选择环境 C++(GDB/LLDB) -> 进入 launch.json 文件
将 "configurations" 的 "program" 设置路径: ${workspaceFolder}/build/bin/demo6



    
    
# 工程内的库,CMAKE_CURRENT_SOURCE_DIR是CMakeLists.txt的目录
set(  变量名字  
	${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/lib.cpp
)
# 工程外的库,CMAKE_SOURCE_DIR是工程根目录
include_directories( 
	${CMAKE_SOURCE_DIR}/目录名/目录名
)

# 查找目录,在target_link_libraries中库名要加"",查找的文件为"lib库名.a"
link_directories(
	${CMAKE_SOURCE_DIR}/目录名/目录名
)


# 增加可执行文件

预处理——编译——汇编——链接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LkASEiod-1613654054907)(https://i.loli.net/2021/02/18/uCn3xSGhbzNLAM9.png)]

每一级目录都有 CMakeLists.txt 文件。

简单样例

project( helloworld )

# 工程内的库,CMAKE_CURRENT_SOURCE_DIR是CMakeLists.txt的目录
set(  变量名字  
	${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/lib.cpp
)
# 工程外的库,CMAKE_SOURCE_DIR是工程根目录
include_directories( 
	${CMAKE_SOURCE_DIR}/目录名/目录名
)

# 查找目录,在target_link_libraries中库名要加"",查找的文件为"lib库名.a"
link_directories(
	${CMAKE_SOURCE_DIR}/目录名/目录名
)


# 增加可执行文件
add_executable( helloworld 
	$(变量名字)
) 

# 动态库、静态链接库
target_link_libraries(
	helloworld
	库名  # 值得是子目录里面camke的project
	# 动态库
	库名 dynamic
)


创建库

# 静态库
project( base64 )

set(  src 
	${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/base64.h
)

add_library( base64 STATIC
	${src}
)

# 动态库
project( base64_dynamic )

ADD_DEFINITIONS( -DBASE64_SHARED )

set(  src 
	${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/base64.h
)

add_library( base64_dynamic SHARED
	${src}
)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Zocleiw-1613654054909)(E:\personal\C++\pics\Cmake\image-20210102221305159.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hcWpNmmT-1613654054911)(E:\personal\C++\pics\Cmake\image-20210102221415947.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NbYxpN74-1613654054913)(E:\personal\C++\pics\Cmake\image-20210102221431682.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMXuz54E-1613654054914)(E:\personal\C++\pics\Cmake\image-20210102221519268.png)]

主体框架

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U9rSkhOd-1613654054916)(E:\personal\C++\pics\Cmake\image-20210102221603883.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZusFp4Mi-1613654054916)(E:\personal\C++\pics\Cmake\image-20210102221844282.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hVZXlWeR-1613654054916)(E:\personal\C++\pics\Cmake\image-20210102221904211.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zQCsH0z7-1613654054917)(E:\personal\C++\pics\Cmake\image-20210102221928104.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7xz67MA3-1613654054917)(E:\personal\C++\pics\Cmake\image-20210102222006470.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EAfgvuiW-1613654054918)(E:\personal\C++\pics\Cmake\image-20210102223035771.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i7juKbCY-1613654054918)(E:\personal\C++\pics\Cmake\image-20210102223308206.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5w8DM11S-1613654054919)(E:\personal\C++\pics\Cmake\image-20210102223509143.png)]

静态库:链接其他文件时用

动态链接库:运行时加载

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rFFPJzqd-1613654054919)(E:\personal\C++\pics\Cmake\image-20210102223620013.png)]

cmake

CMake中的变量

  • 变量和含义

    常用变量含义
    PROJECT_NAME工程名变量
    PROJECT_SOURCE_DIR顶层的项目目录
    PROJECT_BINARY_DIR使用cmake的路径
    CMAKE_ROOTCMAKE安装的根目录
    CMAKE_BUILD_TYPE编译类型:emptyDebugRelease
    CMAKE_SOURCE_DIR顶层的CMakeLists.txt所在路径
    CMAKE_BINARY_DIR顶层的CMakeLists.txtbuild所在目录
    CMAKE_<LANG>_COMPILER设定某个语言LANG的编译器,比如g++
    CMAKE_INSTALL_PREFIX指令install的路径
    CMAKE_CURRENT_SOURCE_DIR当前CMakeLists.txt所在路径
    CMAKE_CURRENT_BINARY_DIR当前CMakeLists.txtbuild所在目录
    EXECUTABLE_OUTPUT_PATH可执行文件输出路径
    LIBRARY_OUTPUT_PATH库输出路径
  • 个别变量解释
    源码目录结构

    ```bash
        jmudou   
        ├── build.sh
        ├── CMakeLists.txt
        └── muduo
           └── base
               ├── Atomic.h
               ├── CMakeLists.txt
               ├── copyable.h
               ├── tests
               │   ├── Atomic_unittest.cc
               │   ├── CMakeLists.txt
               │   └── Timestamp_unittest.cc
               ├── Timestamp.cc
               ├── Timestamp.h
               └── Types.h
    ```
    
    三个`CMakeLists.txt`输出
    
    ```cmake
        jmuduo/CMakeLists.txt: 
            PROJECT_SOURCE_DIR= 11/jmuduo
            PROJECT_BINARY_DIR= 11/build/debug 
            CMAKE_SOURCE_DIR= 11/jmuduo            # 指定顶层的CMakeLists.txt的,因此所有的都相同
            CMAKE_BINARY_DIR= 11/build/debug
            # 上面四项都是与整个项目相关,因此都是一致的。
            CMAKE_CURRENT_SOURCE_DIR= 11/jmuduo     # 指定当前层的CMakeLists.txt的,因此与层相关
            CMAKE_CURRENT_BINARY_DIR= 11/build/debug
    
        muduo/base/test/CMakeLists.txt: 
            PROJECT_SOURCE_DIR= 11/jmuduo
            PROJECT_BINARY_DIR= 11/build/debug
            CMAKE_SOURCE_DIR= 11/jmuduo
            CMAKE_BINARY_DIR= 11/build/debug
            CMAKE_CURRENT_SOURCE_DIR= 11/jmuduo/muduo/base/tests
            CMAKE_CURRENT_BINARY_DIR= 11/build/debug/muduo/base/tests
    
        muduo/base/CMakeLists.txt: 
            PROJECT_SOURCE_DIR= 11/jmuduo
            PROJECT_BINARY_DIR= 11/build/debug
            CMAKE_SOURCE_DIR= 11/jmuduo
            CMAKE_BINARY_DIR= 11/build/debug
            CMAKE_CURRENT_SOURCE_DIR= 11/jmuduo/muduo/base
            CMAKE_CURRENT_BINARY_DIR= 11/build/debug/muduo/base
    ```
    

函数

所有的函数列表中,<>表示的参数必须要有,[]表示的参数为可选。

  • set
    可以设置三个类型的变量值:正常变量,cache variable、环境变量。

    • Normal Variable:set(<variable> <value>... [PARENT_SCOPE])
    • cacheset(<variable> <value>... CACHE <type> <docstring> [FORCE])
    • envset(ENV{<variable>} [<value>])
  • ***OPTION***:提供用户可以选择的选项

    • 格式:option(<variable> "description" [initial value])

    • 比如:

          option(
              USE_MYPATH
              "user path"
              ON
          )
      
  • aux_source_directory

    • 语法:aux_source_directory(<dir> <variable>)
    • 查找目录dir下的所有源文件(即.c, .cpp, .cc等文件),并将名称保存到 variable 变量
  • add_subdirectory

    • add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
    • 添加一个将被编译的子目录。指明CMakeLists.txt所在目录下包含了一个子目录source_dir。这样source_dir下的源文件和CMakeLists.txt等也会被处理。
  • target_link_libraries

    • target_link_libraries(exec libs)
    • 表示可执行程序exec需要链接到一个名为libs的链接库。
  • add_library

    • add_library (name dir)
    • 用在目录dir下的源文件生成一个名为name的静态链接库:libname.a
  • configure_file

    • 加入一个配置头文件,用于处理 CMake 对源码的设置

          configure_file (
              "${PROJECT_SOURCE_DIR}/config.h.in" # config.h.in文件目录
              "${PROJECT_BINARY_DIR}/config.h"    # config.h 生成的头文件目录
          )
      

      在配置文件config.h中,配置相关项,比如options中的USE_MYPATH

          #cmakedefine USE_MYMATH
      
  • include

    • include(file [optional]):读取CMake的相关文件。
    • include(moudle [optional])
      the file with name .cmake is searched in the CMAKE_MODULE_PATH
  • include_directories

        include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
    

    添加指定目录到编译器搜索路径。

  • install
    使用:cmake之后,sudo make install就可以执行相应的库和头文件的安装。

    • TARGET格式

      install(TARGETS targets...
              [[ARCHIVE|LIBRARY|RUNTIME]
              [DESTINATION <dir>]
              [PERMISSIONS permissions...]
              [CONFIGURATIONS [Debug|Release|...]]
              [COMPONENT <component>]  
              [OPTIONAL]
              ] [...])
      
    • targets的类型
      可以安装的库有[ARCHIVE|LIBRARY|RUNTIME]三种:
        1) 可执行程序视为runtime
        2) 静态库视为archieve
        3) Module Library视为library
        4) 共享库和平台有关

    • 参数

      • DESTINATION
        指定一个文件将要被安装的目录。如果给的是一个全路径,那么就直接使用;如果是相对路径,默认是相对CMAKE_INSTALL_PREFIX,其值默认是/usr/local/
          1) 头文件:inclide
          2) 可执行文件:bin
          3) 库:lib

      • PERMISSIONS: 指定安装文件的权限:
          1) user : OWNER_READ, OWNER_WRITE, OWNER_EXECUTE
          2) group:GROUP_READ, GROUP_WRITE, GROUP_EXECUTE
          3) other:WORLD_READ, WORLD_WRITE, WORLD_EXECUTE
          4) uid :SETUID, and SETGID

      • CONFIGURATIONS:为安装规则建立一个配置文件列表。

  • install

    • FILES格式

      INSTALL(FILES files... 
              DESTINATION <dir>
              [PERMISSIONS permissions...]
              [CONFIGURATIONS [Debug|Release|...]]
              [COMPONENT <component>]
              [RENAME <name>] [OPTIONAL])
      
      • files:即文件名
  • 测试

    • ***enanle_testing()***:启动测试

    • ***add_test(testname Exename arg1 arg2 ...)***:

      • 需要先运行测试程序enanle_testing(),这个指令才有效。
      • Exename是可执行程序名,参数arg1, arg2
    • set_tests_properties(...)

      • 括号内格式:(Exename [Exename2...] PROPERTIES prop1 value1 prop2 value2),其中PROPERTIES是固定的单词不能改
      • Exename设置属性,如果没有这个属性,就报错,有如下属性:
          1) WILL_FAIL:如果设置为true,那么会反转测试结果的pass/fail标志。
          2) PASS_REGULAR_EXPRESSION: 匹配正则表达式,只少有一个匹配,则pass
          2) FAIL_REGULAR_EXPRESSION: 匹配正则表达式,则fail
    • 宏测试

          macro(<name> [arg1 [arg2 [arg3 ...]]])
              COMMAND1(ARGS ...)
              COMMAND2(ARGS ...)
              ...
          endmacro(<name>)
      

      就类似于写一个函数,用宏实现,调用:name(arg1,arg2,...)

  • 设置项目的版本号

    • 在顶层的CMakeLists.txt中:

          # 加入版本号是 1.0
          set (Project_VERSION_MAJOR 1) # 主版本号
          set (Project_VERSION_MINOR 0) # 副版本号
      
    • 在配置文件config.h.in中设置:

          #define Project_VERSION_MAJOR @Project_VERSION_MAJOR@
          #define Project_VERSION_MINOR @Project_VERSION_MINOR@
      
    • main函数中就可以直接使用这两个宏,代表版本号:

          printf("Version %d.%d\n",
                  Project_VERSION_MAJOR,
                  Project_VERSION_MINOR);
      
  • 生成安装包
    需要利用CPack工具,也是CMake提供的工具。

    + 先在顶层`CMakeLists.txt`中末尾添加:
    
      ```cmake
          # 构建一个 CPack 安装包
          include(InstallRequiredSystemLibraries)
          set (CPACK_RESOURCE_FILE_LICENSE
              "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
          set (CPACK_PACKAGE_VERSION_MAJOR "${Project_VERSION_MAJOR}")
          set (CPACK_PACKAGE_VERSION_MINOR "${Project_VERSION_MINOR}")
          include (CPack)
      ```
    
      + 导入`InstallRequiredSystemLibraries`模块,以便之后导入`CPack`模块
      + 设置一些`CPack`相关变量,包括版权信息和版本信息,其中版本信息用了上一节定义的版本号
      + 导入`CPack`模块
    
    + 生成二进制安装包:`cpack -C CPackConfig.cmake`
    
    + or 生成源码安装包
    
      + 格式:`cpack -C CPackSourceConfig.cmake`  
      	在执行该命令的目录下得到:
    
      	  ```bash
      	      # Demo8是项目名
      	      $ ls -l | grep Demo8
      	      -rwxrwxrwx 1 szz szz  8631 Dec 12 22:03 Demo8-1.0.1-Linux.sh
      	      -rw-rw-r-- 1 szz szz  3709 Dec 12 22:03 Demo8-1.0.1-Linux.tar.gz
      	      -rw-rw-r-- 1 szz szz  4982 Dec 12 22:03 Demo8-1.0.1-Linux.tar.Z
      	````
    
      + 安装:`sh Demo8-1.0.1-Linux.sh`。  
      	默认的安装路径:
    
      	  ```c
      	      By default the Demo8 will be installed in:
      	      "~/Study/SystemProgram/CMakeExe/Demo8/build/Demo8-1.0.1-Linux"
      	      
      	      Do you want to include the subdirectory Demo8-1.0.1-Linux?
      	      Saying no will install in: "~/Study/SystemProgram/CMakeExe/Demo8/build" [Yn]: 
      	  ```
    
      + 运行。安装后,就运行该程序
    
      	```bash
      	    $ ./Demo8-1.0.1-Linux/bin/Demo 2 5
      	    Now we use our own Math library. 
      	    2 ^ 5 is 32
      	```
    
      + Demo8-1.0.1-Linux下的目录结果
    
      	```bash
      	    Demo8-1.0.1-Linux
      	    ├── bin
      	    │   └── Demo        # 可执行程序
      	    ├── include         # 头文件
      	    │   ├── config.h
      	    │   └── MathFunctions.h
      	    └── lib             # 静态库
      	        └── libMathFunctions.a
      	```
    
  • target_compile_definitions

    • 格式

          target_compile_definitions(<target>
                                    <INTERFACE|PUBLIC|PRIVATE> [items1...]
                                    [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
      
    • 作用:编译时定义的宏

  • string

    • REPLACE

          string(REPLACE 
                <match_string> <replace_string> 
                <output_variable> <input> [<input>...])
      

      将所有input中出现的match_String替换为replace_string,并且将结果存在output_variable

  • message

        message([<mode>] "message to display" ...)
    
    • 显示信息给用户
      mode取决于信息的类型:

      • STATUS:以简洁的方式显示用户感兴趣的信息。

            message(STATUS 
                    "CXX_FLAGS = " 
                    ${CMAKE_CXX_FLAGS} 
                    " " 
                    ${CMAKE_CXX_FLAGS_${BUILD_TYPE}}
                    )
        

        可以理解为printf,把后面的几个信息以空格相间,然后打印出来。显示结果为(和上面对应分成三段):

            CXX_FLAGS = 
            
            -g -D_FILE_OFFSET_BITS=64 -Wall -Wextra 
            -Werror -Wconversion -Wno-unused-parameter 
            -Wold-style-cast -Woverloaded-virtual 
            -Wpointer-arith -Wshadow -Wwrite-strings 
            -march=native -rdynamic 
            
            -O0
        
  • find_package

        find_package(<PackageName> 
                    [version] [EXACT] [QUIET] [MODULE]
                    [REQUIRED] [[COMPONENTS] [components...]]
                    [OPTIONAL_COMPONENTS components...]
                    [NO_POLICY_SCOPE])
    

    主要是寻找和加载外部项目。如果PackageName找到了,PackageName-found会显出,当没有找到时,默认显示
    PackageName-not found。通过模式的选择,可以处理在没有找到包时的解决方案。

    • QUIET:不显示有用信息,
    • REQUIRED:报错
  • find_path

        find_path (<VAR> name0|NAMES name1 [path1 path2 ...])
    

    用以寻找包含着name1文件的目录,如果找到了结果存储在VAR,没有找到结果结果是VAR-not found。成功时,变量被清除find_path再次搜索,没有成功,fin_path再次以相同的变量被调用时搜索。

  • find_library
    同上find_path

        find_library (<VAR> name0|NAMES name1 [path1 path2 ...])
    
    • OPTIONS
      • NAMES
        library指定一个或多个可能的名字。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值