如何在Windows上使用CMake运行超过8K符号的命令?

时间:2017-08-15 12:04:44

标签: windows cmd cmake

在MS Windows中cmd.exe命令行长度限制为8192个字符。当调用一个接受多个文件作为参数的工具时,很容易得到太长的命令行,比如

git update-index --assume-unchanged file1 file2 file3 ...

如果在调用CMake的add_custom_target() / add_custom_command()函数时出现这种情况该怎么办?

1 个答案:

答案 0 :(得分:2)

我们KDE在breeze-icons项目中偶然发现了这个问题。它广泛使用符号链接,在Windows上我们必须以某种方式将它们解析为真实文件。这是在ECMWinResolveSymlinks.cmake模块中完成的。

实现时我必须解决长命令行问题。我将长文件列表拆分为"列表",其中每个子列表都短于8192.由于CMake中的列表是用;分隔的纯字符串,因此您无法轻松创建诸如"列表列表"之类的东西。我不得不使用另一个分隔符:来区分间接级别。之后,我在每个子列表的循环中调用git。拆分代码如下所示:

# In functions like _checkout_symlinks() the command line can become too lengthy for Windows.
# So we partition it, but in a hacky way due to CMake doesn't have list of lists.
function(_portioned_list outvar)
  list(LENGTH ARGN arglen)

  if(arglen EQUAL 0)
    set(${outvar} "" PARENT_SCOPE)
    return()
  endif()

  set(init)
  set(tail)
  math(EXPR range "${arglen} - 1")
  foreach(i RANGE ${range})
    list(GET ARGN ${i} v)
    string(LENGTH "${init}" initlen)
    string(LENGTH ${v} vlen)
    math(EXPR sumlen "${initlen} + ${vlen}")
    if(sumlen LESS 8192)
      list(APPEND init ${v})
    else()
      list(APPEND tail ${v})
    endif()
  endforeach()

  _portioned_list(tail_portioned ${tail})
  string(REPLACE ";" ":" init "${init}") # Generally this is not safe, because filepath can contain ':' character. But not on Windows. Phew.
  set(${outvar} ${init} ${tail_portioned} PARENT_SCOPE)
endfunction()

可以像:

一样使用
file(GLOB ${dir}/* files)
_portioned_list(portioned_files ${files})
foreach(fs IN LISTS portioned_files)
  # convert back to CMake list
  string(REPLACE ":" ";" fs ${fs})
  execute_process(COMMAND ${somecommand} ${fs}
endforeach()