linux更新cmake版本(sudo pacman -S cmake)
零、cmake的构建步骤
- 步骤
1)第一步是cmake -B build,成为配置阶段(configure),这时候只检测环境并生成构建规则,会在build目录下生成本地构建系统能识别的项目文件(Makefile或是.sln)
1)这个阶段可以通过-D设置缓存变量。第二次配置时,之前的-D添加任然会被保留
2)第二步是cmake --build build ,成为构建阶段(build),这时候才实际调用编译器来编译代码
1)cmake -B build -DCMAKE_INSTALL_PREFIX=/opt/openvdb-8.0
设置安装路径为/opt/openvdb-8.0(会安装到/opt/openvdb-8.0/lib/libopenvdb.so)
2)cmake -B build -DCMAKE_BUILD_TYPE = Release
设置构建模式为发布模式
3)cmake -B build
第二次配置没有-D参数,但是之前-D设置的变量都会保存
(此时缓存里仍有之前定义的CMAKE_BUILD_TYPE和CMAKE_INSTALL_PREFIX)
一、Cmake -B和–build指令(跨平台兼容)
1)cmake -B build
在源码目录用-B直接创建build目录并生成build/Makefile,免去了先创建目录再切换进去制定源码目录的麻烦
2)cmake --build build -j4 (统一了不同平台,linux上会调用make,Windows会调用devenv.exe)
自动调用本地的构建系统在build里面构建,即 :
make -C build -j4
3)sudo cmake --build build --target install
调用本地的构建系统执行install这个目标,即安装
二、Cmake -D 选项:制定配置变量(又称缓存变量,前面有提到)
三、Cmake -G选项 制定要用的生成器
- 说明
1)linux默认cmake使用Unix Makefile生成器;windows默认是vs2019生成器;MacOS系统默认是Xcode生成器
2)可以用-G参数改用别的生成器,例如cmake -GNinja -B build
3)ninja是专为性能优化的构建系统,和cmake都是行业标准
4)pip install ninja
5)ninja是跨平台的
6)MSBuild是单线程的,比较慢
四、正式开始
1)第一章 添加源文件(add_executable生成可执行文件)
add_executable( [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL] source1 [source2 …])
生成可执行文件
add_executable(bin_file_name ${SRC_LIST})
- 法一
main.cpp是文件,main是执行文件
add_executable(main main.cpp)
- 法二
add_executable(main)
target_sources(main PUBLIC main.cpp)
假如多个源文件
add_executable(main)
target_sources(main PUBLIC main.cpp opther.cpp)
- 法三:使用变量来存储
add_executable(main)
set(source main.cpp other.cpp)
target_sources(main PUBLIC {sources})
- 法四(对比法三是增加了批量获取当前目录的.h和.c文件)
add_executable(main)
file(GLOB source *.cpp *.h)
target_sources(main PUBLIC {sources})
备注
GLOB会自动遍历所有文件
CONFIGURE_DEPENDS 每次编译都会重新查找文件
2)第二章添加子目录的文件aux_source_directory
GLOB只能指定一个目录,这里指定了当前目录和当前目录下mylib目录的所有文件
aux_source_directory (dir VAR)
扫描一个目录下所有的源代码文件并将列表存储在一个变量中
aux_source_directory(. DIR_LIB_SRCS)
- 法一
add_executable(main)
aux_source_directory(. sources)
aux_source_directory(mylib sources)
target_sources(main PUBLIC {sources})
- 法二
add_executable(main)
file(GLOB_RECURSE sources CONFIGURE_DEPENDS *.cpp *.h)
target_sources(main PUBLIC {sources})
GLOB_RECURSE当前目录下递归搜索,但是这个会把build目录下生成的临时cpp文件也加进来
解决方法:
把源码统一放到src目录下
3)第三章 项目配置变量
(1)CMAKE_BUILD_TYPE 设置构建的类型,调试模式还是发布模式
模式区别:
1)Debug 调试模式,完全不优化,生成调试信息,方便调试系统
2)Release 发布模式,优化程度最高,性能最佳,但是编译比debug慢
3)MinSizeRel 最小体积发布,不完全优化,减少二进制体积
4)RelWithDeblnfo 带调试信息发布,生成的文件比Release更大,因为带有调试的符号信息
5)默认情况下CMAKE_BUILD_TYPE 为空字符串,这时相当于Debug
- 补充:设置默认构建模式的默认值
(2)project 初始化项目信息,并把当前CMakeLists.txt所在位置作为根目录
对于MSVC,会在buile里面生成hellocmake.sln作为“”“IDE眼中的项目”。
(4)四个目录的区别
(5)其他相关变量
(6)projecet的初始化:LANGUAGES字段
-
可以一开始就设置编译的文件语言
-
或者是一开始就声明为空,后面再启用这些语言
(7)CMAKE_CXX_STANDARD变量设置C++标准
-
CMAKE_CXX_STANDARD_REQUIRED是BOOL类型,可以为ON或OFF,默认OFF
表示是否一定要支持你制定的C++标准,为ON则发现不支持就报错,用了ON更安全 -
CMAKE_CXX_EXTENSIONS也是BOOL类型,默认为ON。设为ON表示启动GCC特有的一些扩展功能;OFF则关闭GCC的扩展功能,只能用标准的C++
特别备注
:
1)要兼容其他编译器(如MSVC)的项目,都会设为OFF防止不小心用了GCC才有的特性。
2)用CMAKE_CXX_FLAGS
来添加-std=c++17有个缺陷:
GCC用户指定了-std=C++17,不兼容MSVC
(8)project的初始化:VERSION字段(设置程序版本号)
- 补充:获取任意一个分支项目的版本号
(9)可以嵌套的${}表达式
1)$
{PROJECT_NAME}求值的结果是hellocmake
2)所以$
{$
{PROJECT_NAME}_VERSION}相当于$
{hellocmake_VERSION}进一步求值的结果也就是刚刚指定的0.2.3了
(10)cmake_minimun_required指定最低所需的CMAKE版本
(11)add_dependencies
add_dependencies (target-name depend-target1 depend-target2 …)
定义target依赖的其他target,确保target在构建之前,其依赖的target已经构建完毕
(12)target_compile_options设置编译参数
这个命令是为某个需要编译的目标增加编译选项
target_compile_options(<target> [BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
arget_compile_options会增加选项(options)到COMPILE_OPTIONS或者INTERFACE_COMPILE_OPOTIONS目标属性(也就是增加编译选项的意思)。这些选项会在编译给定目标()时使用,给定目标必须已经通过add_executable()或者add_library()这种命令添加到项目中,并且不能是别名目标。
- 使用举例
cmake_minimum_required(VERSION 3.10)
project(cmake_gcc_options_try_c C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
add_executable(cmake_gcc_options_try_c main.c)
target_compile_options(cmake_gcc_options_try_c
PUBLIC -W -Wall -Wextra -pedantic -pedantic-errors)
备注:CMake选择编译器及设置编译器选项
(1)命令行选择编译器
cmake .. -DCMAKE_CXX_COMPILER=/usr/local/gcc/bin/g++
-wall表示要生成所有警告信息
(2)在配置文件tool.cmake中指定编译器
set (CMAKE_C_COMPILER "/usr/local/gcc/bin/gcc")
set (CMAKE_CXX_COMPILER "/usr/local/gcc/bin/g++")
注:这两条命令应该放在文件的开始位置(cmake_minimum_required命令之下,其他命令之上),否则可能无效。
(3)区别
综上,对于一些在整个项目中通用的编译选项可以使用add_compile_options命令来添加比较方便,对于各个模块中的独立选项则使用CMAKE_CXX_FLAGS变量更好。
这里用到的CMAKE_CXX_FLAGS变量是只针对C++编译器的选项,对于其他编程语言,只要替换部分就可以,在当前cmake版本(3.17.2)中支持如下语言:
CMAKE_C_FLAGS:C语言编译器选项,对应于环境变量CFLAGS
CMAKE_CXX_FLAGS:C++语言编译器选项,对应于环境变量CXXFLAGS
CMAKE_CUDA_FLAGS:CUDA语言编译器选项,对应于环境变量CUDAFLAGS
CMAKE_Fortran_FLAGS:Fortran语言编译器选项,对应于环境变量FFLAGS
(4)实用举例
# 判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
add_compile_options(-std=c++11)
message(STATUS "optional:-std=c++11")
endif(CMAKE_COMPILER_IS_GNUCXX)
cmake_minimum_required(VERSION 3.5)
project(eignface_demo)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lstdc++ -lm")
set(OPENCV_LIB opencv_highgui opencv_objdetect opencv_core opencv_imgproc opencv_contrib)
set(SRC_CPP EigenFace.cpp)
set(GCC_PATH D:/code/mingw)
if (HISI)
set(CMAKE_C_COMPILER $(GCC_PATH)/bin/gcc.exe)
set(CMAKE_CXX_COMPILER $(GCC_PATH)/bin/g++.exe)
set(TOOLCHAIN_DIR "/opt/hisi-linux-nptl/arm-hisiv100-linux/target")
set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_DIR})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/bin/arm-hisiv100nptl-linux-g++)
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/bin/arm-hisiv100nptl-linux-gcc)
include_directories(./hisi_opencv/include/)
link_directories(./hisi_opencv/lib/)
message("-- Use hisi compile demo")
add_executable(eignface_hisi ${SRC_CPP})
target_link_libraries(eignface_hisi ${OPENCV_LIB})
else()
set(CMAKE_C_COMPILER "/usr/bin/gcc")
include_directories(./gcc_opencv/include/)
link_directories(./gcc_opencv/lib/)
message("-- Use gcc compile demo")
add_executable(eignface_gcc ${SRC_CPP})
target_link_libraries(eignface_gcc ${OPENCV_LIB})
endif()
(13)target_include_directories
该命令可以指定目标(exe或者so文件)需要包含的头文件路径。命名的<目标>必须是由add_executable()或add_library()之类的命令创建的,并且不能是ALIAS目标。通过显式地使用AFTER或BEFORE,就可以在附加和预挂之间进行选择,而不是依赖默认值。
- 备注
需要INTERFACE、PUBLIC和PRIVATE关键字来指定以下参数的范围。PRIVATE和
PUBLIC项将填充<target>的INCLUDE_DIRECTORIES属性。PUBLIC和INTERFACE项将
填充<target>的INTERFACE_INCLUDE_DIRECTORIES属性
- 例子
有如下文件的目录,一个math库,main.cc文件来调用math库,math库实现了一个整数的幂运算
1)math目录的cmakelist
cmake_minimum_required (VERSION 2.8)
project(MathFunctions)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)
# 指定生成 MathFunctions 链接库
add_library (MathFunctions ${DIR_LIB_SRCS})
2)主目录的cmakelist
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (Demo3)
# 添加 math 子目录,输出.a文件到output目录
add_subdirectory(math output)
# 指定生成目标
add_executable(Demo main.cc)
# 将子目录的头文件包含到目标中
target_include_directories(Demo PUBLIC math)
# 添加链接库
target_link_libraries(Demo MathFunctions)
(14)add_library(有对对象库的定义)
使用该命令可以生成(静态/动态)库so或者.a文件,它有两种命令格式
- 用法一:
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[<source>...])
生成一个名为 < name > 的library,在项目中必须是全局唯一的。
1.1、< name > 应该保证在一个项目中的唯一性。
1.2、实际的library文件名基于Native平台的约定规则,比如:lib< name >.a, < name >.lib等
1.3、STATIC,SHARED,MODULE用于指定创建的library类型。
STATIC库:是object文件的归档,用于链接其他targets。
SHARED库:是动态链接,并于运行时加载。
MODULE库:不能链接到其他targets,但是可以用dlopen之类的方法在运行时动态加载。
1.4、如果没有明确指定上述类型,那么如果BUILD_SHARED_LIBS变量值为ON,则默认是SHARED,否则默认STATIC。
1.5、对于SHARED和MODULE类型的库,POSITION_INDEPENDENT_CODE属性自动置为ON。
1.6、EXCLUDE_FROM_ALL:表明该target是否从默认构建target中排除。
1.7、source参数可以使用generator表达式($ <…>)。
- 用法二:
add_library(<name> OBJECT [<source>...])
创建一个object对象库,该对象库只编译源文件,但不链接。
由add_library()或 add_executable()创建的目标可以使用$<TARGET_OBJECTS:name>这样的表达式作为源引用对象,其中,name是对象库的名称。格式如下:
add_library(... $<TARGET_OBJECTS:name> ...)
add_executable(... $<TARGET_OBJECTS:name> ...)
- 使用举例
(大家习惯通过 $<TARGET_OBJECTS:name>引用object文件。其实object library与静态
库,动态库一样,都是可以设置PUBLIC/PRIVATE属性的,再通过target_link_library()命令
链接)
四个文件hello.h,hello.cpp,main.cpp,CMakelist.txt,目录如下
(1)第一种library格式
cmake_minimum_required (VERSION 3.12.1)
project (Demo)
# 生成对象库文件
add_library(hello hello.cpp)
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
# 生成可执行文件
add_executable(Demo main.cpp)
# 链接对象库
target_link_libraries(Demo hello)
(2)第二种格式
cmake_minimum_required (VERSION 3.12.1)
project (Demo)
# 生成对象库文件
add_library(hello OBJECT hello.cpp)
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
# 添加编译选项 -Wall
target_compile_options(hello PUBLIC -Wall)
# 生成可执行文件
add_executable(Demo main.cpp $<TARGET_OBJECTS:hello>)
# 添加头文件目录
target_include_directories(Demo PUBLIC ${CMAKE_SOURCE_DIR}/public)
(3)第三种写法
cmake_minimum_required (VERSION 3.12.1)
project (Demo)
# 生成对象库文件,不链接
add_library(hello OBJECT hello.cpp)
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
# 添加编译选项 -Wall
target_compile_options(hello PUBLIC -Wall)
# 生成可执行文件
add_executable(Demo main.cpp)
# 链接对象库
target_link_libraries(Demo hello)
(15)cmake的-G 参数
CMake默认根据平台选择一个生成器。通常,默认生成器足以让用户继续构建软件。用户可以使用-G选项覆盖默认生成器:
$ cmake .. -G Ninja
or
cmake -G "Visual Studio 15 2017 Win64" ..
- 补充:在类Unix系统(包括Mac OS X)上,默认情况下使用Unix Makefiles生成器。该生成器的一个变体也可以在Windows的各种环境中使用,比如NMake Makefiles和MinGW Makefiles生成器。这些生成器生成一个Makefile变量,可以用make、gmake、nmake或类似的工具执行而Visual Studio生成器可以针对不同的体系结构。可以使用-A选项指定目标架构:
cmake .. -G "Visual Studio 2019" -A x64
cmake .. -G "Visual Studio 16" -A ARM
cmake .. -G "Visual Studio 16 2019" -A ARM64
(16)cmake的macro宏定义
- 定义
定义一个名为的宏,它接受名为,…等一系列的参数。macro与endmacro之间列出的命令,在宏被调用之前不会执行
macro(<name> [<arg1> ...])
<commands>
endmacro()
- 使用
macro(bar)
foreach(arg IN LISTS ARGN)
<commands>
endforeach()
endmacro()
- 调用
foo()
Foo()
FOO()
cmake_language(CALL foo)
(17)foreach的用法
cmake命令之foreach添加链接描述
(18)set的用法
(19)list的用法
五、标准的CMakeLists.txt模板
#1、设置所需cmake版本
cmake_minimum_required(VERSION 3.21)
#2、设置cmake编译语言
set(CMAKE_CXX_STADARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#3、设置项目名字
project(freshman LANGUAGES C CXX)
#4、如果源目录和输出目录是同一个目录就发出警告
if (PROJECT_BINARY_DIR STREQUAL PROJECT_SOURCE_DIR)
message(WARNING "The binary directory of CMake Cannot be the same as source directory")
endif()
#5、如果没设置构建模式,就设置为发布模式
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
#6、WIN特有的bug,windows.h会定义下面两个宏,导致std::min和std::max用不了,所以要添加
if(WIN32)
#向C/C++编译器添加-D定义,参数之间用空格分隔
add_definitions(-DNOMINMAX -D_USE_MATH_DEFINES)
endif()
#7、增加编译缓存,编译得更快
if(NOT MSVC)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
message(STATUS "Found CCache: ${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_PROGRAM})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_PROGRAM})
endif()
endif()
六、链接库文件find_target()
1)链接静态库(动态库在win上有很多坑,建议用静态库)
2)链接动态库
3)对象库(优点:可以指定不同的编译选项)
- 对象库优点
4)add_libray无参数(STATIC或SHARED),根据BUILD_SHARED_LIBS决定使用的是静态库还是动态库
-
BUILD_SHARED_LIBS默认值
BUILD_SHARED_LIBS默认为STATIC,ON相当于SHARED,OFF相当于STATIC
-
设定BUILD_SHARED_LIBS的默认值
5)常见坑点1:动态库无法链接静态库(因为动态库地址是变化的,静态库链接执行文件地址是不变的)
- 错误展示
- 解决方法
.
1)静态库变成对象库
2)静态库也生成PIC的代码
这里设置otherlib静态库为ON,表示生成PIC代码
6)windows使用静态链接库
- 在实现的地方加上xport
- 在声明的地方加上import
7)windwos链接动态库坑2:dll和exe不在同一目录导致的
- 原因
windwos只会找当前exe所在目录的dll,找不到就报错 - 解法1
把dll所在位置加入到PATH环境变量里面去,一劳永逸 - 解法2
把这个dll,以及这个dll所依赖的其他dll,全部拷贝到exe文件同一目录下 - 解法3(手动吧dll和exe放在同一目录很麻烦)
1)做法1
2)做法2:不用微软系统,暗转Arch Linux系统
8)完整演示
find_package(TBB)
if((TBB_FOUND)
message(STATUS "TBB found at: ${TBB_DIR}")
target_link_libraries(main PUBLIC TBB::tbb) #找到就链接上
target_compile_definitions(main PUBLIC WITHTBB)
else()
message(WARNING "TBB not found: using serial for")
endif()
- 若编译进去了
- 或找到TBB::tbb对象
七、设置对象的属性
1)set_property一个一个的设置属性
2)set_target_properties设置多个属性
这个命令是设置目标的属性,该命令的语法是列出想要更改的所有目标,然后提供接下来想要设置的值。您可以使用该命令任何所需的键值对,然后使用get_property()或get_target_property()命令提取它。
- 备注(以下区别是等价的)
1)set_properties和set_target_properties
add_executable(myplay ${DIR_SRCS})
# set_property
set_property(TARGET myplay PROPERTY CXX_STANDARD 14)
# set_target_properties
set_target_properties(myplay PROPERTIES CXX_STANDARD 14)
2)使用举例
设置目标的属性值,例如设置输出库名为 static => libstatic.a
set_target_properties(static_demo PROPERTIES OUTPUT_NAME "static")
八、链接第三方库
1)案例:链接tbb这个库
- 这样直接指定tbb的
缺点
1)这样指定,CMake会让连接器在系统的库目录里查找tbb,他会找到/usr/lib/libtbb.so这个系统自带的,但对于没有一个固定库安装位置的Windows系统并不适用
2)此外,还要求tbb的头文件就在/usr/include这个系统默认的头文件目录里面,才能include<tbb/parallel_for.h>不出错,如果tbb的头文件在其他地方就需要再增加一个target_invlude_directories
设置额外的头文件查找目录
2)find_package不需要手动添加库目录
- 对比现在和古代cmake
- 带CONFIG有什么区别?
- 举例QT(QT需要制定用了哪个组件)
- 举例TBB
- 为Cmake找到QT5怎么办?(WINODWS)
1)法一(目录写死):
2)法二:设置QT5_DIR这个变量
3)法三(推荐,命令行指定):
4)设置环境变量
九、cmake的目录/与\的问题
cmake的目录的路径分隔符始终都是/,即使在windows上,也要把所有的/改成/,这是出于跨平台的考虑,CMake在调用MSVC的时候转换成\。可以放心的使用${X}/bin来实现和Python的os.path.join(x,‘bin’)一样的效果
十、message
1)简单打印message
message( " 123123456....")
2)带上状态信息 STATUS
message(STATUS " 123123456....") #status表示做了啥,check或detecting等等
3)WARNING 打印出警告信息
message(WARNING " 123123456....")
4)AUTHOR_WARNING 可以用-Wno-dev关闭
5)message(FATAL_ERROR “…”) 表示错误信息,直接终止CMAKE的运行
6)message(SEND_ERROR “…”) 表示是错误信息,但之后的语句任然执行但是最后也报错
7)message 可以打印变量
- 补充没加引号
- message默认打印第一个参数,例如打印了第一个FATAL_ERROR
十一、缓存和变量
1)cmake两次编译的原因和结果(第一次缓存,第二次直接开始编译)
2)二次编译的坑,百分之99的cmake错误可以用删build解决(或者说删除build/CMakeCache.txt就可以了)
rm -rf build
3)设置缓存变量(官方默认错误的方法)
4)设置缓存变量(正确方法)
5)命令行-D参数太硬核了,更有图形化的缓存编辑器
ccmake-gui -B build
6)可以指定FORCE强制set更新缓存
7)缓存变量类型(举例4种),并用案例说明BOOL类型的缓存变量使用
- 案例(添加一个BOOL类型的缓存变量,用于控制要不要开启某特性)
8)CMake中Option的坑,直接改option是错误的
- 1)法一:
cmake -B build -DWITH_TBB:BOOL=OFF
-
2)法二:
-
法三:使用普通变量,没有定义时就设为默认值
十二、跨平台和编译器
1)CMake中给.cpp定义一个宏
定义了MY_MACRO等于233
2)判断当前系统的名字
3)其他简写变量:WIN32,APPLE,UNIX,ANDROID,IOS、MSVC\等
- 其他简写判断
4)使用生成器表达式,简化成一条指令
- 补充,不同平台都支持,中间可以逗号分隔
5)可以判断当前使用的C++编译器
- 生成器表达式判断编译器
- 或者用生成器表达式做复杂的逻辑判断
6)命令行指定编译器
-
补充选择编译器的检测
-
补充cmake4vim插件
十三、分支和判断
1)BOOL类型的值(ON和OFF)
2)if的特点,不需要加${}
(1)MATCHES 表示匹配
-
若加了${},由于找不到Hello变量名的存在,所以这个变量会被字符串代替并查找(有缺陷)
-
解决办法(加括号防止被当做变量名字)
3)Cmake指令部分大小写,${}内的变量名是分大小写的
两个x不一样,蓝颜色部分
十四、变量和作用域
1)父模块的变量会传给子模块
2)子模块变量不传给父模块
-
但是如果需要网上传变量,那就需要用set的PARENT_SCOPE选项
-
不建议用缓存变量向外传变量,这样不光父模块可见了,父模块的父模块,到处可见了
下面是错误做法
3)除了父子模块有独立作用域的
4)环境变量的访问方式: $ENV{xx}
-
举例$x
-
举例环境变量访问
5)缓存变量的访问方式: $CACHE{xx}
- 补充:当找不到局部变量,会自动去找缓存变量
6)用if(DEFINEED XX) 判断某变量是否存在
- 补充:空字符串不等于未定义,空字符串等价于false
- 判断某环境变量是否存在
- 设置环境变量
x = 3 env
- 查看环境变量
env