美文网首页
Cmake命令之file

Cmake命令之file

作者: Domibaba | 来源:发表于2023-08-26 14:24 被阅读0次

    file是文件操作命令,用于文件或路径的操作,结果也会在文件系统上进行存储。因此,与cmake_path命令只是语义概念上对路径的处理不同,file会与文件系统进行实际的交互。

    一、命令格式

    file命令包含文件系统路径转换传输归档七个子命令,格式如下:

    子命令:读
      file(READ <filename> <out-var> [...])
      file(STRINGS <filename> <out-var> [...])
      file(<HASH> <filename> <out-var>)
      file(TIMESTAMP <filename> <out-var> [...])
      file(GET_RUNTIME_DEPENDENCIES [...])
    
    子命令:写
      file({WRITE | APPEND} <filename> <content>...)
      file({TOUCH | TOUCH_NOCREATE} [<file>...])
      file(GENERATE OUTPUT <output-file> [...])
      file(CONFIGURE OUTPUT <output-file> CONTENT <content> [...])
    
    子命令:文件系统
      file({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])
      file(MAKE_DIRECTORY [<dir>...])
      file({REMOVE | REMOVE_RECURSE } [<files>...])
      file(RENAME <oldname> <newname> [...])
      file(COPY_FILE <oldname> <newname> [...])
      file({COPY | INSTALL} <file>... DESTINATION <dir> [...])
      file(SIZE <filename> <out-var>)
      file(READ_SYMLINK <linkname> <out-var>)
      file(CREATE_LINK <original> <linkname> [...])
      file(CHMOD <files>... <directories>... PERMISSIONS <permissions>... [...])
      file(CHMOD_RECURSE <files>... <directories>... PERMISSIONS <permissions>... [...])
    
    子命令:路径转换
      file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])
      file(RELATIVE_PATH <out-var> <directory> <file>)
      file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)
    
    子命令:传输
      file(DOWNLOAD <url> [<file>] [...])
      file(UPLOAD <file> <url> [...])
    
    子命令:锁
      file(LOCK <path> [...])
    
    子命令:归档
      file(ARCHIVE_CREATE OUTPUT <archive> PATHS <paths>... [...])
      file(ARCHIVE_EXTRACT INPUT <archive> [...])
    
    

    二、命令使用

    在开始举例说明前,命令操作的文件/目录树说明如下:

    ├── CMakeLists.txt
    ├── copy                # 该目录用于演示COPY子命令
    │   └── myfile_read     # COPY子命令拷贝的文件结果
    ├── myfile_download     # 用于演示DOWNLOAD下载子命令
    ├── myfile_read         # 用于演示READ子命令
    ├── myfile_write        # 用于演示WRITE子命令
    └── result.tar          # 用于演示归档ARCHIVE_CREATE子命令
    
    
    读文件内容
    file(READ myfile_read out_var)
    message("Content of myfile_read is: ${out_var}")
    
    

    运行结果:
    Content of myfile_read is: This is a file for test

    写文件:如果文件不存在,会创建文件。
    set(content "Write hello to file.")
    file(WRITE myfile_write ${content}) # 文件不存在会创建,直接覆盖文件写
    file(READ myfile_write out_var)
    message("Content of myfile_write: ${out_var}")
    file(APPEND myfile_write "\nAnother line.") # 文件不存在会创建,在文件末尾追加写
    file(READ myfile_write out_var)
    message("Content of myfile_write: ${out_var}")
    
    

    运行结果:
    Content of myfile_write: Write hello to file.
    Content of myfile_write: Write hello to file.
    Another line.

    文件系统操作
    file(COPY myfile_read DESTINATION ./copy) # 将文件拷贝到./copy目录下
    
    

    运行cmake .后,myfile_read会被复制一份到./copy目录下。

    路径转换
    file(REAL_PATH "./copy" out_var BASE_DIRECTORY "/bin") # ./copy目录基于base目录/bin的路径
    message("./copy relative base /bin result: ${out_var}")
    
    

    运行结果:
    ./copy relative base /bin result: /bin/copy

    文件传输操作
    file(DOWNLOAD "https://sh.rustup.rs" myfile_download) # 下载文件
    
    

    运行cmake .命令后,会将文件下载到本地,并命名为myfile_download

    归档
    file(ARCHIVE_CREATE OUTPUT "result.tar" PATHS myfile_read myfile_write myfile_download FORMAT gnutar) # 将myfile_read myfile_write myfile_download打包成tar文件格式
    
    

    运行cmake .命令后,会生成一个result.tar文件,使用tar tvf result.tar命令查看文件内容:

    -rw-r--r-- 0 XXX YYY 24 5 10 07:42 myfile_read
    -rw-r--r-- 0 XXX YYY 34 5 10 08:23 myfile_write
    -rw-r--r-- 0 XXX YYY 21102 5 10 08:23 myfile_download

    三、子命令详解

    3.1 子命令:读
    • file(READ <filename> <variable> [OFFSET <offset>] [LIMIT <max-in>] [HEX])

    说明:从文件<filename>中读取内容,并将结果存入到<variable>中。可以指定从文件偏移<offset>处开始读,最多读取字节数(bytes)通过<max-in>参数指定。HEX选项会将读取结果转换成十六进制表示(在读取二进制数据的时候非常有用),且十六进制的输出将按照小写输出(也就是或说输出a b c d e f)。

    # CMakeLists.txt
    
    # 从myfile_read的第5个字节偏移处开始读,读取4个字节
    file(READ myfile_read content OFFSET 8 LIMIT 6)
    message("read from myfile_read at offset 8 and read 6 bytes: ${content}")
    
    # 读取结果转换成十六进制表示,十六进制表示中的ABCDEF将按照小写输出
    file(READ myfile_read content_hex OFFSET 8 LIMIT 6 HEX)
    message("read from myfile_read at offset 8 and read 6 bytes (HEX output): ${content_hex}")
    
    

    myfile_read文件的原始内容为:THIS IS A FILE FOR READ TEST.

    运行结果:
    read from myfile_read at offset 8 and read 6 bytes: A FILE
    read from myfile_read at offset 8 and read 6 bytes (HEX output): 412046494c45

    • file(STRINGS <filename> <variable> [<options>...])

    说明
      按照ASCII编码从文件中读取并存储在<variable>中。换行符会被忽略,如果是二进制数据,也会被忽略。默认情况下是以换行符对读取的内容进行分割,每一行读取为输出结果序列的一个子项,不同子项以分号分割(也可以使用长度进行分割等,见下详解)。该命令的选项详解如下:

    • LENGTH_MAXIMUM <max-len>:每次读取不超过给定长度的字符串,如果某一行的字符超过指定长度,会以指定长度对该行进行分割读取。
    • LENGTH_MINIMUM <min-len>:每次读取不少于给定长度的字符串,如果某一行的字符串长度低于指定长度,则不会读取到结果中。
    • LIMIT_COUNT <max-num>:限制提取的字符串子项个数,例如默认情况下是以换行分割读取,假如<max-num>5,如果输入文件有6行,那么只会读取前5行。
    • LIMIT_INPUT <max-in>:限制从输入文件的读取的字节数。
    • LIMIT_OUTPUT <max-out>:限制存入到<variable>中的总字节数,当解析得到最后一个字符串子项长度累加后超过指定长度时候,最后一个字符串子项会丢弃。
    • NEWLINE_CONSUME:将换行符作为字符串内容的一部分,而不是在换行时停止读取。
    • NO_HEX_CONVERSIONIntel十六进制和MotorolaS-record文件会自动转换成二进制,除非指定了本选项。
    • REGEX <regex>:读取匹配指定正则表达式的字符串。
    • ENCODING <encoding-type>3.1版本中引入。按照指定的编码读取字符串,当前支持的编码包括:UTF-8, UTF-16LE, UTF-16BE, UTF-32LE, UTF-32BE。此外,如果未指定本选项并且文件本身有字节顺序标记Byte Order Mark,那么会按照文件的字节顺序标记进行解析。
    # CMakeLists.txt
    file(STRINGS myfile_read content_s) # 默认会以换行将文件的内容分割成多个子项,按照分号序列的方式存储
    message("read myfile_read as STRINGS: ${content_s}")
    
    # 限制每次提取的字符串长度不超过10
    file(STRINGS myfile_read content_s LENGTH_MAXIMUM 10)
    message("read myfile_read as STRINGS - LMax: ${content_s}")
    
    # 限制每次提取的字符串长度至少为10,小于10会舍弃
    file(STRINGS myfile_read content_s LENGTH_MINIMUM 10)
    message("read myfile_read as STRINGS - LMin: ${content_s}")
    
    # 限制提取的字符串子项数量最多为2个
    file(STRINGS myfile_read content_s LIMIT_COUNT 2)
    message("read myfile_read as STRINGS - LCount: ${content_s}")
    
    # 限制只提取文件的前35个字符
    file(STRINGS myfile_read content_s LIMIT_INPUT 35)
    message("read myfile_read as STRINGS - LInput: ${content_s}")
    
    # 限制输出的总长度为45
    file(STRINGS myfile_read content_s LIMIT_OUTPUT 45)
    message("read myfile_read as STRINGS - LOutput: ${content_s}")
    
    # 不以换行进行分割,输出结果包含换行
    file(STRINGS myfile_read content_s NEWLINE_CONSUME)
    message("read myfile_read as STRINGS - Contain CR: ${content_s}")
    
    

    myfile_read文件的原始内容有三行:
    THIS IS A FILE FOR READ TEST.
    new line1
    new line2

    运行结果为:
    read myfile_read as STRINGS: THIS IS A FILE FOR READ TEST.;new line1;new line2
    read myfile_read as STRINGS - LMax: THIS IS A ;FILE FOR R;EAD TEST.;new line1;new line2
    read myfile_read as STRINGS - LMin: THIS IS A FILE FOR READ TEST.
    read myfile_read as STRINGS - LCount: THIS IS A FILE FOR READ TEST.;new line1
    read myfile_read as STRINGS - LInput: THIS IS A FILE FOR READ TEST.;new l
    read myfile_read as STRINGS - LOutput: THIS IS A FILE FOR READ TEST.;new line1
    read myfile_read as STRINGS - Contain CR: THIS IS A FILE FOR READ TEST.
    new line1
    new line2

    • file(<HASH> <filename> <variable>)

    说明:计算文件的hash结果,支持的hash算法可以参考string(<HASH>)

    # CMakeLists.txt
    file(MD5 myfile_read content_hash)
    message("Hash of myfile_read is: ${content_hash}")
    
    

    运行结果为:
    Hash of myfile_read is: 6a4ff0158d402b69360ee52ba217faf3

    • file(TIMESTAMP <filename> <variable> [<format>] [UTC])

    说明:将文件的修改时间转换成字符串表示,并存储在结果<variable>中。如果无法获得时间戳,字符串将置为""<format>UTC选项的详细使用方法见string(TIMESTAP)。未使用UTC选项时,返回的时本地时间,默认的格式为年-月-日T时:分:秒;当指定UTC选项时,默认格式为年-月-日T时:分:秒Z

    # CMakeLists.txt
    file(TIMESTAMP myfile_read time_string)
    message("myfile_read modify time is(local time): ${time_string}")
    
    file(TIMESTAMP myfile_read time_string UTC)
    message("myfile_read modify time is(UTC): ${time_string}")
    
    file(TIMESTAMP myfile_read time_string "%B.%d %Y-%H:%M:%S-%A")
    message("myfile_read modify time is(format: 月.日 年-时:分:秒-周): ${time_string}")
    
    

    运行结果为:
    ***myfile_read modify time is(local time): 2022-05-11T08:14:34
    myfile_read modify time is(UTC): 2022-05-11T00:14:34Z
    myfile_read modify time is(format: 月.日 年-时:分:秒-周): May.11 2022-08:14:34-Wednesday*******

    • file(GET_RUNTIME_DEPENDENCIES[RESOLVED_DEPENDENCIES_VAR <deps_var>] [UNRESOLVED_DEPENDENCIES_VAR <unresolved_deps_var>] [CONFLICTING_DEPENDENCIES_PREFIX <conflicting_deps_prefix>] [EXECUTABLES [<executable_files>...]] [LIBRARIES [<library_files>...]] [MODULES [<module_files>...]] [DIRECTORIES [<directories>...]] [BUNDLE_EXECUTABLE <bundle_executable_file>] [PRE_INCLUDE_REGEXES [<regexes>...]] [PRE_EXCLUDE_REGEXES [<regexes>...]] [POST_INCLUDE_REGEXES [<regexes>...]] [POST_EXCLUDE_REGEXES [<regexes>...]] [POST_INCLUDE_FILES [<files>...]] [POST_EXCLUDE_FILES [<files>...]])

    说明:这个命令比较复杂,且根install命令有关,后续等install命令完成后返回来补充。

    # CMakeLists.txt
    
    

    运行结果为:


    3.2 子命令:写
    • file(WRITE|APPEND <filename> <content>...)

    说明:将内容写入到指定的文件,如果文件不存在则创建文件;如果文件已经存在,WRITE选项会覆盖原文件,而APPEND则将内容添加在原文件的末尾。此外,文件filename路径中不存在的目录也会被创建出来。

    # CMakeLists.txt
    file(WRITE myfile_wirte "write one line\n")
    file(READ myfile_wirte content)
    message("file(user wirte): ${content}")
    
    file(WRITE myfile_wirte "write another line\n")
    file(READ myfile_wirte content)
    message("file(user wirte): ${content}")
    
    file(APPEND myfile_wirte "append one line\n")
    file(READ myfile_wirte content)
    message("file(user append): ${content}")
    
    

    运行结果为:
    file(user wirte): write one line

    file(user wirte): write another line

    file(user append): write another line
    append one line

    • file(TOUCH|TOUCH_NOCREATE [<files>...])

    说明3.12版本引入。

    • TOUCH会创建一个内容为空的新文件,如果文件已经存在,那么该命令不会对文件的内容有任何影响,只会将文件的访问或者修改时间更新为该命令调用时间。
    • TOUCH_NOCREATE选项不会创建新文件,如果文件不存在则会忽略,如果文件存在,那么则更新文件的访问或者修改时间为该命令调用的时间。

    该命令可以指定多个文件。

    # CMakeLists.txt
    file(TIMESTAMP myfile_read modify_time)
    message("myfile_read modify time is: ${modify_time}")
    file(TOUCH_NOCREATE myfile_read) # 如果myfile_read不存在,那么不会创建文件,如果myfile_read存在,则更新其访问/修改时间
    file(TIMESTAMP myfile_read modify_time)
    message("after touch(but not create) myfile_read modify time is: ${modify_time}")
    file(TOUCH mynewfile) # 创建新文件
    file(TIMESTAMP mynewfile modify_time)
    message("create mynewfile time is: ${modify_time}")
    
    

    运行结果为:
    myfile_read modify time is: 2022-05-11T08:14:34
    after touch(but not create) myfile_read modify time is: 2022-05-12T22:18:51
    create mynewfile time is: 2022-05-12T22:18:51

    • file(GENERATE OUTPUT output-file <INPUT input-file|CONTENT content> [CONDITION expression] [TARGET target] [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS | FILE_PERMISSIONS <permissions>...] [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])

    说明:该命令跟生成表达式有关,待完成生成表达式后补充。

    3.3 子命令:文件系统
    • file(GLOB <variable> [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS] [<globbing-expressions>...])

    说明
      生成能匹配表达式<globbing-expressions>的一组文件,并将结果存入到<variable>中,结果按照字典序进行排列(3.6版本及之后)。Globbing expressions与正则表达式类似,但是比正则表达式要简单很多。
      在WindowsmacOS系统中,Globbing expressions是大小写不敏感的(在匹匹配为之前会将文件名和Globbing expressions转换为小写);其他系统Globbing expressions是大小写敏感的。
      具体的选项解析如下:

    • LIST_DIRECTORIES:默认情况下,结果会将目录也列出来,如果将LIST_DIRECTORIES设置为false,那么目录会被忽略,只会列出文件存入结果中。
    • RELATIVE:指定该选项,将返回匹配文件相对于RELATIVE指定的路径。RELATIVE必须指定一个绝对路径。
    • CONFIGURE_DEPENDS:指定该选项,CMake会在构建时重新运行该命令。
    # CMakeLists.txt
    file(GLOB result "matches/*") # 列出目录matches下的所有文件,带全路径
    message("all files in matches: ${result}")
    file(GLOB result "matches/*.txt") # 匹配matches下后缀为txt的文件,带全路径
    message("match *.txt result: ${result}")
    file(GLOB result RELATIVE /XXX/YYY/ZZZ/matches "myfile*")
    message("list file: ${result}") # 查找当前目录./下的myfile开头的文件,返回相对于./matches路径的结果
    
    

    运行结果:
    *all files in ./matches: /XXX/YYY/ZZZ/matches/log1.txt;/XXX/YYY/ZZZ/matches/log2.txt;/XXX/YYY/ZZZ/matches/log3.txt;/XXX/YYY/ZZZ/matches/log4.txt;/XXX/YYY/ZZZ/matches/log5.txt;/XXX/YYY/ZZZ/matches/test1.dat;//XXX/YYY/ZZZ/matches/test2.dat;/XXX/YYY/ZZZ/matches/test3.dat;/XXX/YYY/ZZZ/matches/test4.dat
    match .txt result: /XXX/YYY/ZZZ/matches/log1.txt;/XXX/YYY/ZZZ/matches/log2.txt;/XXX/YYY/ZZZ/matches/log3.txt;/XXX/YYY/ZZZ/matches/log4.txt;/XXX/YYY/ZZZ/matches/log5.txt
    list file: ../myfile_download;../myfile_read;../myfile_wirte;../myfile_write

    • file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS] [LIST_DIRECTORIES true|false] [RELATIVE <path>] [CONFIGURE_DEPENDS] [<globbing-expressions>...])

    说明
      与GLOB区别是,该子命令会递归的遍历匹配目录的所有子目录,然后寻找匹配的文件。

    • file(MAKE_DIRECTORY [<directories>...])

    说明:创建指定目录,如果指定的目录的路径中有不存在的目录,也会一并创建。

    file(MAKE_DIRECTORY ./dir1/dir2/dir3/mynewdir) # dir1、dir2、dir3、mynewdir目录都会被创建
    
    
    • file(REMOVE|REMOVE_RECURSE [<files>...])

    说明:移除指定的文件,REMOVE_RECURSE模式将会移除给定的文件和目录(即使目录是非空的)。如果给定的文件不存在,也不会有错误的信息提示,如果文件是以相对路径提供,那么会以当前目录作为参考。
    3.15版本之前,如果是空的文件路径输入,会将空路径当成相对路径,以当前目录作为参考,删除当前目录的内容;而3.15及之后的版本,空的文件输入会被忽略,并给出一个提示信息。

    # CMakeLists.txt
    file(REMOVE "./dir1/dir2/dir3/mynewdir/testfile1") # 删除指定文件testfile1
    file(REMOVE_RECURSE "./dir1/dir2/dir3/mynewdir/testfile2") # 删除指定文件testfile2
    file(REMOVE_RECURSE "./dir1/dir2/dir3/mynewdir") # 删除指定目录mynewdir,会递归删除mynewdir目录下所有的文件和子目录,也会删除mynewdir目录本身
    file(REMOVE "") # 3.15及之后的版本会忽略该语句并给出提示,假设该语句位于CMakeLists.txt文件的第23行,提示信息见下
    
    

    运行结果:
    CMake Warning (dev) at CMakeLists.txt:23 (file):
    Ignoring empty file name in REMOVE.
    This warning is for project developers. Use -Wno-dev to suppress it.

    • file(RENAME <oldname> <newname> [RESULT <result>] [NO_REPLACE])

    说明:将指定的文件或者目录从旧路径<oldname>移动到新路径<newname>下,如果存在相同的名称文件或者目录会进行自动替换。

    • RESULT <result>3.21版本引入。如果命令执行成功,将<result>变量置为0,否则<result>变量存储错误信息(CMake不会产生错误)。如果未指定该选项,当执行不成功,CMake会产生一个错误。
    • NO_REPLACE3.21版本引入。默认情况下新路径下已经存在文件或目录,会自动进行替换,指定该选项则不会对相同的文件或目录进行替换,而是会产生一个错误(如果指定了RESULT选项,NO_REPLACE会存储到RESULT指定的变量中。)
    # CMakeLists.txt
    file(RENAME ./rename_test/old/oldfile6 ./rename_test/new/) # 会提示错误:No such file or directory
    file(RENAME ./rename_test/old/oldfile6 ./rename_test/new/ RESULT info) # 不会提示错误,错误信息No such file or directory会存储到info中
    message("operation result: ${info}")
    file(RENAME ./rename_test/old/oldfile1 ./rename_test/new/newfile1 RESULT info) # 执行成功,info结果为0
    message("operation result: ${info}")
    file(RENAME ./rename_test/old/olddir1/oldfile4 ./rename_test/new/olddir1/oldfile4 NO_REPLACE RESULT info) # 有NO_REPLACE选项,因此无法进行替换操作,info中存储的是NO_REPLACE
    message("operation result: ${info}")
    file(RENAME ./rename_test/old/olddir1/oldfile4 ./rename_test/new/olddir1/oldfile4 RESULT info) # 执行成功,info结果为0
    message("operation result: ${info}")
    
    

    上述命令操作的文件树结构为:

    ├── CMakeLists.txt
    ├── rename_test
    │   ├── new
    │   │   └── olddir1
    │   │       └── oldfile4
    │   └── old
    │   │   └── olddir1
    │   │       └── oldfile4
    │   |   ├── olddir1
    │   |   ├── oldfile2
    │   |   └── oldfile3
    
    
    • file(COPY_FILE <oldname> <newname> [RESULT <result>] [ONLY_IF_DIFFERENT])

    说明3.21版本引入。将文件从<oldname>复制到<newname>,不支持目录的复制。如果<oldname>是一个符号链接,那么会读取<oldname>实际链接的内容,并将实际指向的内容复制到<newname>中。该命令支持的几个选项解析如下:

    • RESULT <result>:如果执行成功,<result>变量内容为0,执行出错该变量内容为具体的错误信息。如果未指定该选项且命令执行出错,那么CMake会抛出错误并停止构建。
    • ONLY_IF_DIFFERENT:如果目标文件<newname>已经存在,并且<newname>文件的内容与<oldname>内容一样,那么不做替换操作,这样就可以避免更新<newname>的时间戳。
    # CMakeLists.txt
    file(COPY_FILE mydir mydir_copy) # 不支持目录,如果不指定RESULT,CMake构建会提示错误:file COPY_FILE cannot copy a directory
    file(COPY_FILE mydir mydir_copy RESULT result)
    message("Copy dir result: ${result}") # 复制不成功,CMake构建不会提示错误,错误信息会存到result中
    
    file(COPY_FILE myfile myfile_copy RESULT result) # myfile内容为"This is a file for testing."
    file(TIMESTAMP myfile_copy ts)
    message("Copy file result: ${result}, timestamp: ${ts}") # 复制成功,结果为0
    
    execute_process(COMMAND sleep 5)
    file(COPY_FILE myfile myfile_copy RESULT result) # 复制相同的内容,未指定ONLY_IF_DIFFERENT选项,仍会执行赋值操作,注意时间戳打印不一致
    file(TIMESTAMP myfile_copy ts)
    message("Copy file for same content result: ${result}, timestamp: ${ts}")
    
    execute_process(COMMAND sleep 5)
    file(COPY_FILE myfile myfile_copy RESULT result ONLY_IF_DIFFERENT) # 复制相同的内容,指定ONLY_IF_DIFFERENT选项,不会执行复制动作,因此时间戳没有变化
    file(TIMESTAMP myfile_copy ts)
    message("Copy file for same content result: ${result}, timestamp: ${ts}")
    
    file(WRITE myfile_copy "Rewrite file content.")
    execute_process(COMMAND sleep 5)
    file(COPY_FILE myfile myfile_copy RESULT result ONLY_IF_DIFFERENT) # 由于目标文件内容和源文件内容不一致了,会执行复制操作
    file(TIMESTAMP myfile_copy ts)
    message("Copy file for different content result: ${result}, timestamp: ${ts}")
    
    

    执行结果为:
    CMake Error at CMakeLists.txt:4 (file): file COPY_FILE cannot copy a directory

    /XXX/YYY/ZZZ/mydir

    Copy dir result: cannot copy a directory
    Copy file result: 0, timestamp: 2022-05-15T08:07:51
    Copy file for same content result: 0, timestamp: 2022-05-15T08:07:56
    Copy file for same content(use ONLY_IF_DIFFEREBT)result: 0, timestamp: 2022-05-15T08:07:56
    Copy file for different content result: 0, timestamp: 2022-05-15T08:08:06
    -- Configuring incomplete, errors occurred!

    • file(<COPY|INSTALL> <files>... DESTINATION <dir> [NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS] [FILE_PERMISSIONS <permissions>...] [DIRECTORY_PERMISSIONS <permissions>...] [FOLLOW_SYMLINK_CHAIN] [FILES_MATCHING] [[PATTERN <pattern> | REGEX <regex>] [EXCLUDE] [PERMISSIONS <permissions>...]] [...])

    说明:如果只是简单的文件复制,可以使用前面的file(COPY_FILE)命令。相对于file(COPY_FILE),本命令是一个更复杂的命令,它可以复制文件、目录、符号链接等。输入文件的目录是相对于当前源文件路径,目的文件路径是相对于当前构建路径。

    • COPY子命令会保存输入文件的时间戳,如果目的文件存在,那么会将目的文件的时间戳修改成和输入文件一致。默认情况下(USE_SOURCE_PERMISSIONS选项)也会将输入文件的权限传递给目的文件,除非通过NO_SOURCE_PERMISSIONS选项显示禁止。
    • INSTALL子命令与COPY子命令稍微不一样的地方是:INSTALL子命令会打印状态信息,并且默认情况下不会将输入文件的权限传递给目的文件(也就是默认为NO_SOURCE_PERMISSIONS选项)。install()命令就是使用file(INSTALL)命令来产生安装脚本文件的。在3.22版本以后,还可以给CMAKE_INSTALL_MODE环境变量赋值来改变file(INSTALL)的默认复制行为。
    • FOLLOW_SYMLINK_CHAIN:如果指定了该选项,会递归的解析符号链接,直到找到最终的文件,并且每一个符号链接都会在指定的目的目录创建出来,并且都指向同个目录下创建的最终文件。这个选项在类Unix系统下特别有用,在这些系统中,库文件都是以版本号命名的符号链接形式安装,由不具体的版本指向具体的版本。
    • 对于权限,FILES_MACHINGPATTERNREGEXEXCLUDE请参考install(DIRECTORY)
    目录结构为:
    copy_test
    ├── dest
    └── source
        ├── dir_for_copy
        │   └── file1
        └── libs
            ├── mylib.so
            ├── mylib.so.1 -> mylib.so
            ├── mylib.so.1.2 -> mylib.so.1
            └── mylib.so.1.2.3 -> mylib.so.1.2
    
    
    # CMakeLists.txt
    file(COPY copy_test/source/dir_for_copy/file1 DESTINATION copy_test/dest/) # 假定file1的权限是660,使用COPY,默认拷贝后的文件权限也是660,查看 执行结果1
    file(INSTALL copy_test/source/dir_for_copy/file1 DESTINATION copy_test/dest/) # 假定file1的权限是660,使用INSTALL,默认拷贝后的文件权限是644(系统默认文件创建权限),查看 执行结果2
    
    file(COPY copy_test/source/dir_for_copy DESTINATION copy_test/dest/) # 拷贝dir_for_copy目录
    
    file(COPY copy_test/source/libs/mylib.so DESTINATION copy_test/dest/libs FOLLOW_SYMLINK_CHAIN) # 递归拷贝mylib.so的链接并创建,查看 执行结果3
    file(COPY copy_test/source/libs/mylib.so DESTINATION copy_test/dest/libs_nosymlink) # 只会拷贝单个mylib.so文件,查看 执行结果4
    
    
    • 执行结果1:
      复制前的file1权限:
      -rw-rw---- copy_test/source/dir_for_copy/file1
      复制后的file1权限:
      -rw-rw---- copy_test/dest/file1
    • 执行结果2:
      复制前的file1权限:
      -rw-rw---- copy_test/source/dir_for_copy/file1
      复制后的file1权限:
      -rw-r--r-- copy_test/dest/file1
      并且使用INSTALL会打印处中间的执行状态信息:
      -- Up-to-date: /XXX/YYY/ZZZ/copy_test/dest//file1
    • 执行结果3:
      FOLLOW_SYMLINK_CHAIN选项,复制后的目录结构,会将整个链接递归创建出来:
      copy_test
      ├── dest
      │ ├── libs
      │ │ ├── mylib.so -> mylib.so.1
      │ │ ├── mylib.so.1 -> mylib.so.1.2
      │ │ ├── mylib.so.1.2 -> mylib.so.1.2.3
      │ │ └── mylib.so.1.2.3
      └── source
      ├── dir_for_copy
      │ └── file1
      └── libs
      ├── mylib.so -> mylib.so.1
      ├── mylib.so.1 -> mylib.so.1.2
      ├── mylib.so.1.2 -> mylib.so.1.2.3
      └── mylib.so.1.2.3
    • 执行结果4:
      没有FOLLOW_SYMLINK_CHAIN选项,只会将链接文件当成普通文件复制:
      copy_test
      ├── dest
      │ └── libs_nosymlink
      │ └── mylib.so -> mylib.so.1
      └── source
      ├── dir_for_copy
      │ └── file1
      └── libs
      ├── mylib.so -> mylib.so.1
      ├── mylib.so.1 -> mylib.so.1.2
      ├── mylib.so.1.2 -> mylib.so.1.2.3
      └── mylib.so.1.2.3
    • file(SIZE <filename> <variable>)

    说明3.14版本引入,计算文件的大小,要求指向文件的路径是合法的路径,且文件本身是可读的。

    # CMakeLists.txt
    set(filename "myfile_read")
    file(SIZE ${filename} sz)
    message("file ${filename} size is : ${sz} bytes")
    
    

    执行结果为:
    file myfile_read size is : 50 bytes

    • file(READ_SYMLINK <linkname> <variable>)

    说明3.14版本引入。读取链接文件并将该链接文件指向的文件路径保存到变量<variable>中,如果链接文件不存在,CMake会抛出一个错误。需要注意的是,该命令返回的时候链接文件原始指向的文件路径,并且是相对路径。

    # CMakeLists.txt
    set(linkname "/XXX/YYY/ZZZ/copy_test/source/libs/mylib.so")
    file(READ_SYMLINK ${linkname} result) # 复制的是mylib.so执行的mylib.so.1,且不会递归解析
    message("Get link result: ${result}")
    
    # 可以通过如下方法来获取绝对路径
    if (NOT IS_ABSOLUTE "${result}")
        get_filename_component(dir "${linkname}" DIRECTORY)
        set(result "${dir}/${result}")
    endif()
    message("Get link result after add absolute path: ${result}")
    
    

    执行结果为:
    Get link result: mylib.so.1
    Get link result after add absolute path: /XXX/YYY/ZZZ/copy_test/source/libs/mylib.so.1

    • file(CREATE_LINK <original> <linkname> [RESULT <result>] [COPY_ON_ERROR] [SYMBOLIC])

    说明3.14版本引入,创建一个指向<original>的链接文件<linkname>,如果<linkname>已经存在,则会被覆盖。默认创建的是硬链接(硬链接要求原始文件存在且不是目录)。

    • RESULT <result>:如果执行成功,<result>变量内容为0,执行出错该变量内容为具体的错误信息。如果未指定该选项且命令执行出错,那么CMake会抛出错误并停止构建。
    • SYMBOLIC:创建软连接。
    • COPY_ON_ERROR:如果创建链接文件失败,那么复制文件。主要用于<original><linkname>在不同的驱动器或者挂在点的场景,此时创建硬链接会失败,用此选项可以复制文件。
    # CMakeLists.txt
    set(original "myfile_read")
    file(CREATE_LINK ${original} myfile_read_hardlink)
    file(CREATE_LINK ${original} myfile_read_softlink SYMBOLIC)
    
    

    执行结果为,硬链接用ls -l查询,数字是2
    -rw-r--r-- 2 myfile_read
    -rw-r--r-- 2 shengyi staff 50 5 12 22:18 myfile_read_hardlink
    lrwxr-xr-x 1 shengyi staff 11 5 17 08:52 myfile_read_softlink -> myfile_read

    • file(CHMOD <files>... <directories>... [PERMISSIONS <permissions>...] [FILE_PERMISSIONS <permissions>...] [DIRECTORY_PERMISSIONS <permissions>...])

    说明3.19版本引入,修改指定文件或目录的权限,合法的权限设置选项有:OWNER_READ, OWNER_WRITE, OWNER_EXECUTE, GROUP_READ, GROUP_WRITE, GROUP_EXECUTE, WORLD_READ, WORLD_WRITE, WORLD_EXECUTE, SETUID, SETGID。此外,PERMISSIONSFILE_PERMISSIONSDIRECTORY_PERMISSIONS只能单独使用或者按照如下方式组合使用:

    • PERMISSIONS:所有指定的文件或目录的权限都会被修改。
    • FILE_PERMISSIONS:只改变指定文件的权限。
    • DIRECTORY_PERMISSIONS:只改变指定目录的权限。
    • 同时指定PERMISSIONSFILE_PERMISSIONS:文件的权限会被FILE_PERMISSIONS指定的权限覆写。
    • 同时指定PERMISSIONSDIRECTORY_PERMISSIONS:目录的权限会被DIRECTORY_PERMISSIONS指定的权限覆写。
    • 同时指定FILE_PERMISSIONSDIRECTORY_PERMISSIONSFILE_PERMISSIONS指定的权限应用于文件,DIRECTORY_PERMISSIONS指定的权限应用于目录。
    # CMakeLists.txt
    # 备注:原始权限 file_test 644; dir_test 755
    file(CHMOD file_test PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE WORLD_READ WORLD_WRITE) # 修改权限为666
    #file(CHMOD dir_test PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_WRITE GROUP_EXECUTE WORLD_READ WORLD_WRITE WORLD_EXECUTE) # 修改权限为777
    
    file(CHMOD file_test PERMISSIONS OWNER_READ GROUP_READ WORLD_READ FILE_PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE WORLD_READ) # FILE_PERMISSIONS指定权限起作用,修改为664
    file(CHMOD dir_test PERMISSIONS OWNER_READ GROUP_READ WORLD_READ DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # DIRECTORY_PERMISSIONS指定的权限起作用,修改为755
    
    
    • file(CHMOD_RECURSE <files>... <directories>... [PERMISSIONS <permissions>...] [FILE_PERMISSIONS <permissions>...] [DIRECTORY_PERMISSIONS <permissions>...])

    说明3.19版本引入,与CHMOD的区别就是CHMOD_RECURSE会递归的修改指定目录下的子目录和文件权限,类似于Linux命令chmod -R

    3.4 子命令:路径转换
    • file(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])

    说明3.19版本中引入。计算指定路径的绝对路径。

    • BASE_DIRECTORY <dir>:如果指定了该选项,且给定待转换的路径<path>是一个相对路径,那么计算<path>相对于<dir>的路径。如果未指定该选项,那么计算<path>相对于CMAKE_CURRENT_SOURCE_DIR环境变量的路径。
    • EXPAND_TILDE3.21版本新增。如果<path>~或以~/开头,那么~会被替换为用户的home目录。home目录的值是从环境变量中得到,在Wndows下,首先会从USERPROFILE环境变量获取,如果未指定该环境变量,则会从HOME环境变量中获取;其他系统只会从HOME环境变量中获取。
    # CMakeLists.txt
    file(REAL_PATH dir_test result BASE_DIRECTORY "/bin")
    message("Conversion result: ${result}")
    
    message("Home path is : $ENV{HOME}")
    file(REAL_PATH "~/dir_test" result BASE_DIRECTORY "/bin" EXPAND_TILDE) # 此时会忽略掉BASE_DIRECTORY指定的值
    message("Conversion result: ${result}")
    
    

    运行结果(macOS系统):
    Conversion result: /bin/dir_test
    Home path is : /Users/user1
    Conversion result: /Users/user1/dir_test

    • file(RELATIVE_PATH <variable> <directory> <file>)

    说明:计算<directory><file>的相对路径,实际上就是怎么从<directory>通过...等切换到<file>。需要注意的是,<directory><file>都必须是绝对路径。

    # CMakeLists.txt
    file(RELATIVE_PATH result /usr/local /etc/passwd)
    message("Conversion result: ${result}")
    
    

    运行结果(macOS系统):
    Conversion result: ../../etc/passwd

    • file(TO_CMAKE_PATH|TO_NATIVE_PATH "<path>" <variable>)

    说明TO_CMAKE_PATH将系统原生路径转换为CMake风格的路径。TO_NATIVE_PATH则反过来,将CMake风格的路径转换为系统原生路径。

    • CMake风格的路径:路径以/作为目录之间的分隔符,且多个目录以;分割的序列。
    • 原生系统路径:在Windows下,以\作为目录分隔符,多个路径以;分隔;而在其他系统,以/作为目录分隔符,多个路径以:分隔。例如在我的macOS下,PATH环境变量(对应系统原生路径)为:/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin

    注意:子命令RELATIVE_PATH, TO_CMAKE_PATHTO_NATIVE_PATH已经分别被cmake_path的子命令RELATIVE_PATHCONVERT ... TO_CMAKE_PATH_LISTCONVERT ... TO_NATIVE_PATH_LIST取代。因此直接参考cmake_path的相关命令。

    3.5 子命令:传输
    • file(DOWNLOAD <url> [<file>] [<options>...]);file(UPLOAD <file> <url> [<options>...])

    说明:从<url>指定的地址下载到本地<file>中。从3.19版本开始,如果没有指定本地文件<file>,那么不会保存文件,这样在检测待下载文件是否存在的时候比较有用。该命令还有许多可选项,解析如下:

    • INACTIVITY_TIMEOUT <seconds>:当超过指定时间没有活动则终止操作(上传/下载)。
    • LOG <variable>:将可读的日志存储到变量中。
    • SHOW_PROGRESS:打印进度信息。
    • STATUS <variable>:存储命令的操作(上传/下载)结果,由长度为2,以;分隔的序列组成,序列的第一个元素是一个以数字表示的结果返回值,第二元素是一个字符串表示错误的信息。如果没有出错,则只返回0
    • TIMEOUT <seconds>:超时后终止操作(上传/下载)。
    • USERPWD <username>:<password>3.7新增,为操作(上传/下载)指定用户名和密码。
    • HTTPHEADER <HTTP-header>3.7新增,为操作(上传/下载)指定http协议的头,该选项可以重复多次使用。
    • NETRC <level>3.11新增,指定操作(上传/下载)是否使用.netrc文件,如果未指定该选项,那么会从变量CMAKE_NETRC中获取,有如下几个级别:IGNORED表示忽略.netrc文件,也是选项的默认值;OPTIONAL表示.netrc文件可选,会优先从URL中获取信息,如果未从URL获取到信息则会从.netrc文件获取;REQUIRED表示使用.netrc文件,忽略URL中的信息。
    • NETRC_FILE <file>3.11新增,指定另一个.netrc文件,来替换home目录下的.netrc文件。如果未指定该选项,则会从CMAKE_NETRC_FILE变量中获取。
    • TLS_VERIFY <ON|OFF>:指定是否校验https URL服务器证书,默认是不验证。如果未指定该选项,则会从CMAKE_TLS_VERIFY变量中获取。3.18版本中增加了对file(UPLOAD)的支持。
    • TLS_CAINFO <file>:为https URL指定一个定制的证书鉴权文件。未指定该选项,则会从CMAKE_TLS_CAINFO变量中获取。3.18版本中增加了对file(UPLOAD)的支持。

    如下选项只支持DOWNLOAD

    • EXPECTED_HASH ALGO=<value>:验证下载的内容的哈希值是否匹配期望的哈希值。ALGO指定file(HASH)支持的HASH算法,如果不匹配下载会出错。注意,如果未指定本地<file>文件,该命令会执行出错。
    • EXPECTED_MD5 <value>:该选项是为了兼容历史选项,实际上可以用上面的命令替代。它校验下载内容的MD5值和期望的是否匹配。如果不匹配下载会出错。注意,如果未指定本地<file>文件,该命令会执行出错。
    3.6 子命令:锁
    • file(LOCK <path> [DIRECTORY] [RELEASE] [GUARD <FUNCTION|FILE|PROCESS>] [RESULT_VARIABLE <variable>] [TIMEOUT <seconds>])

    说明3.2版本新增。锁定<path>指定的文件。

    • DIRECTORY:指定该选项会锁定目录,并会创建一个<path>/cmake.lock文件。
    • GUARD:指定锁定的范围,默认是PROCESS(进程),可选的还有FUNCTION(函数)和FILE(文件)。
    • RELEASE:显式的释放锁。指定该选项时,GUARDTIMEOUT会被忽略。
    • TIMEOUT:指定等待锁定的超时时间,如果值为0,表示只会尝试去锁定一次,其他非0值则是尝试锁定文件的超时时间。如果未指定该选项,则会一直尝试锁定,直到锁定成功或者出错。
    • RESULT_VARIABLE:存储命令执行的结果,如果成功,结果是0,否则存储具体的错误。如果未指定该选项,发生错误时候CMake会终止构建并抛出错误。

    锁只是一个建议项,不能保证锁定后进程也会遵循。也不能对一个文件锁定2次。

    为了验证,我们创建了两个CMakeLists.txt文件对同一个文件/目录进行锁定,目录树如下:
    ├── CMakeLists.txt
    ├── file_lock_test
    │ ├── CMakeLists.txt
    ├── myfile_read
    ├── myfile_read_dir

    # 顶层的CMakeLists.txt:在锁定文件myfile_read和文件夹myfile_read_dir后,睡眠50秒,目的是让file_lock_test下的CMakeLists.txt指定
    file(LOCK myfile_read_dir DIRECTORY RESULT_VARIABLE result)
    message("lock myfile_read_dir result: ${result}")
    file(LOCK myfile_read RESULT_VARIABLE result)
    message("lock myfile_read result: ${result}")
    execute_process(COMMAND sleep 50)
    
    
    # file_lock_test下的CMakeLists.txt
    file(LOCK ../myfile_read_dir DIRECTORY RESULT_VARIABLE result TIMEOUT 0)
    message("lock dir myfile_read_dir result: ${result}")
    file(LOCK ../myfile_read RESULT_VARIABLE result TIMEOUT 10)
    message("lock myfile_read result: ${result}")
    
    

    运行结果:
    首先执行顶层的CMakeLists.txt,出现如下打印说明锁定成功
    lock myfile_read_dir result: 0
    lock myfile_read result: 0

    然后执行file_lock_test下的CMakeLists.txt,出现如下打印说明文件或者目录已经被锁定,命令超时退出了
    lock dir myfile_read_dir result: Timeout reached
    lock myfile_read result: Timeout reached

    3.7 子命令:归档
    • file(ARCHIVE_CREATE OUTPUT <archive> PATHS <paths>... [FORMAT <format>] [COMPRESSION <compression> [COMPRESSION_LEVEL <compression-level>]] [MTIME <mtime>] [VERBOSE])

    说明3.18版本新增。将PATHS指定的文件和目录进行归档,不支持通配符。

    • FORMAT:指定归档的格式,支持7zip, gnutar, pax, paxr, raw, zip,如果未指定该选项,默认归档格式是paxr
    • COMPRESSION:指定压缩方式,7zipzip归档格式已经使用压缩,其他格式默认不压缩,因此需要指定压缩方式,支持的压缩方式有None, BZip2, GZip, XZ, Zstd
    • COMPRESSION_LEVEL3.19版本新增。在指定压缩方式的前提下,可以指定压缩级别,范围是0-9,默认是0
    • VERBOSE:呈现归档命令的输出。
    • MTIME:如果要指定压缩文件的修改时间,指定该选项。

    待压缩文件夹的结构为:
    archive_test
    ├── dir1
    │ └── file4
    ├── file1
    ├── file2
    └── file

    # CMakeLists.txt
    file(ARCHIVE_CREATE OUTPUT result.tar.gz PATHS archive_test FORMAT gnutar COMPRESSION GZip VERBOSE MTIME "2022/5/18 23:59:59")
    
    

    运行结果,并生成了一个result.tar.gz的压缩包:
    archive_test
    archive_test/file3
    archive_test/file2
    archive_test/file1
    archive_test/dir1
    archive_test/dir1/file4

    • file(ARCHIVE_EXTRACT INPUT <archive> [DESTINATION <dir>] [PATTERNS <patterns>...] [LIST_ONLY] [VERBOSE])

    说明3.18版本新增。提取或者显示归档文件的内容。

    • DESTINATION:将归档文件提取到指定路径。
    • PATTERNS:通过该选项,可以从归档文件中提取或者显示指定的文件/目录,支持通配符。
    • LIST_ONLY:显示归档文件内容,不提取。
    • VERBOSE:呈现提取归档文件命令的输出。

    以归档的result.tar.gz为例:

    # CMakeLists.txt
    message("====list========")
    file(ARCHIVE_EXTRACT INPUT result.tar.gz VERBOSE LIST_ONLY) # 显示归档文件内容,不提取
    message("====extract dir1/file4========")
    file(ARCHIVE_EXTRACT INPUT result.tar.gz DESTINATION extract_dir1 PATTERNS "*dir1/*" VERBOSE) # 提取dir1目录下文件到extract_dir1目录
    message("====extract all========")
    file(ARCHIVE_EXTRACT INPUT result.tar.gz DESTINATION extract_dir2 VERBOSE) # 提取全部内容到extract_dir2目录
    
    

    运行结果:
    ====list========
    drwxr-xr-x 0 shengyi staff 0 18 May 23:59 archive_test/
    -rw-r--r-- 0 shengyi staff 8 18 May 23:59 archive_test/file3
    -rw-r--r-- 0 shengyi staff 8 18 May 23:59 archive_test/file2
    -rw-r--r-- 0 shengyi staff 6 18 May 23:59 archive_test/file1
    drwxr-xr-x 0 shengyi staff 0 18 May 23:59 archive_test/dir1/
    -rw-r--r-- 0 shengyi staff 8 18 May 23:59 archive_test/dir1/file4
    ====extract dir1/file4========
    x archive_test/dir1/
    x archive_test/dir1/file4
    ====extract all========
    x archive_test/
    x archive_test/file3
    x archive_test/file2
    x archive_test/file1
    x archive_test/dir1/
    x archive_test/dir1/file4

    extract_dir1目录内容:
    extract_dir1
    └── archive_test
    └── dir1
    └── file4

    extract_dir2目录内容:
    extract_dir2
    └── archive_test
    ├── dir1
    │ └── file4
    ├── file1
    ├── file2
    └── file3

    附录:参考目录

    1. https://cmake.org/cmake/help/latest/command/file.html

    相关文章

      网友评论

          本文标题:Cmake命令之file

          本文链接:https://www.haomeiwen.com/subject/xhqpmdtx.html