【CMake】检测外部库和程序

find_package是用于发现和设置包的CMake模块的命令。这些模块包含CMake命令,用于标识系统标准位置中的包。CMake模块文件称为Find<name>.cmake,当调用find_package(<name>)时,模块中的命令将会运行。

除了在系统上实际查找包模块之外,查找模块还会设置了一些有用的变量,反映实际找到了什么,也可以在自己的CMakeLists.txt中使用这些变量。

 

对于Python解释器,相关模块为FindPythonInterp.cmake(位于<CMake安装路径>/share/cmake-<版本号>/Modules/)附带的设置了一些CMake变量:

  • PYTHONINTERP_FOUND:是否找到解释器
  • PYTHON_EXECUTABLE:Python解释器到可执行文件的路径
  • PYTHON_VERSION_STRING:Python解释器的完整版本信息
  • PYTHON_VERSION_MAJOR:Python解释器的主要版本号
  • PYTHON_VERSION_MINOR :Python解释器的次要版本号
  • PYTHON_VERSION_PATCH:Python解释器的补丁版本号

可以强制CMake,查找特定版本的包。例如,要求Python解释器的版本大于或等于2.7:find_package(PythonInterp 2.7)。

如果在查找位置中没有找到适合Python解释器的可执行文件,CMake将中止配置。

1、Cmake检测python解释器:

 1 cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
 2 project(recipe-01 LANGUAGES NONE)
 3 
 4 find_package(PythonInterp REQUIRED)
 5 
 6 execute_process(
 7   COMMAND
 8       ${PYTHON_EXECUTABLE} "-c" "print('Hello, world!')"
 9   RESULT_VARIABLE _status
10   OUTPUT_VARIABLE _hello_world
11   ERROR_QUIET
12   OUTPUT_STRIP_TRAILING_WHITESPACE
13   )
14 
15 message(STATUS "RESULT_VARIABLE is: ${_status}")
16 message(STATUS "OUTPUT_VARIABLE is: ${_hello_world}")

 

2、CMake检测python库:

C++代码:

 1 #include <Python.h>
 2 #include <sstream>
 3 
 4 int main(int argc, char *argv[])
 5 {
 6 
 7     std::wstringstream wss;
 8     wss << argv[0];
 9     Py_SetProgramName(wss.str().c_str());
10     Py_Initialize();
11     PyRun_SimpleString("from time import time,ctime\n"
12                        "print('Today is',ctime(time()))\n");
13     Py_Finalize();
14     return 0;
15 }

CMakeLists.txt

 1 cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
 2 project(CmakeTest LANGUAGES CXX)
 3 
 4 find_package(PythonInterp REQUIRED)
 5 find_package(Python COMPONENTS Interpreter Development REQUIRED)
 6 
 7 add_executable(${PROJECT_NAME} main.cpp)
 8 
 9 message(STATUS "Python Include: ${Python_INCLUDE_DIRS}")
10 target_include_directories(${PROJECT_NAME}
11   PRIVATE
12       ${Python_INCLUDE_DIRS}
13     )
14 
15 target_link_libraries(${PROJECT_NAME}
16   PRIVATE
17       ${Python_LIBRARIES}
18     )

 

3、CMake检测python的模块和包

python代码:

 1 # @file use_numpy.py
 2 import numpy as np
 3 
 4 # 构建rows * cols的矩阵
 5 def print_ones(rows, cols):
 6     A = np.ones(shape=(rows, cols), dtype=float)
 7     print(A)
 8     # we return the number of elements to verify
 9     # that the C++ code is able to receive return values
10     num_elements = rows*cols
11     return(num_elements)

C++代码:(参考:https://docs.python.org/2/extending/embedding.html#pure-embedded)

 1 /**
 2  * @file main.cpp
 3  */
 4 #include <Python.h>
 5 int main(int argc, char *argv[])
 6 {
 7     PyObject *pName, *pModule, *pDict, *pFunc;
 8     PyObject *pArgs, *pValue;
 9     int i;
10     if (argc < 3)
11     {
12         fprintf(stderr, "Usage: %s pythonfile funcname [args]\n", argv[0]);
13         return 1;
14     }
15     Py_Initialize();
16     PyRun_SimpleString("import sys");
17     PyRun_SimpleString("sys.path.append(\".\")");
18     pName = PyUnicode_DecodeFSDefault(argv[1]);
19     /* Error checking of pName left out */
20     pModule = PyImport_Import(pName);
21     Py_DECREF(pName);
22     if (pModule != NULL)
23     {
24         pFunc = PyObject_GetAttrString(pModule, argv[2]);
25         /* pFunc is a new reference */
26         if (pFunc && PyCallable_Check(pFunc))
27         {
28             pArgs = PyTuple_New(argc - 3);
29             for (i = 0; i < argc - 3; ++i)
30             {
31                 pValue = PyLong_FromLong(atoi(argv[i + 3]));
32                 if (!pValue)
33                 {
34                     Py_DECREF(pArgs);
35                     Py_DECREF(pModule);
36                     fprintf(stderr, "Cannot convert argument\n");
37                     return 1;
38                 }
39                 /* pValue reference stolen here: */
40                 PyTuple_SetItem(pArgs, i, pValue);
41             }
42             pValue = PyObject_CallObject(pFunc, pArgs);
43             Py_DECREF(pArgs);
44             if (pValue != NULL)
45             {
46                 printf("Result of call: %ld\n", PyLong_AsLong(pValue));
47                 Py_DECREF(pValue);
48             }
49             else
50             {
51                 Py_DECREF(pFunc);
52                 Py_DECREF(pModule);
53                 PyErr_Print();
54                 fprintf(stderr, "Call failed\n");
55                 return 1;
56             }
57         }
58         else
59         {
60             if (PyErr_Occurred())
61                 PyErr_Print();
62             fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
63         }
64         Py_XDECREF(pFunc);
65         Py_DECREF(pModule);
66     }
67     else
68     {
69         PyErr_Print();
70         fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
71         return 1;
72     }
73     Py_Finalize();
74     return 0;
75 }

 

CMakeLists.txt内容:

 1 cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
 2 project(CmakeTest LANGUAGES CXX)
 3 
 4 set(CMAKE_CXX_STANDARD 11)
 5 set(CMAKE_CXX_EXTENSIONS OFF)
 6 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 7 
 8 find_package(PythonInterp REQUIRED)
 9 find_package(Python COMPONENTS Interpreter Development REQUIRED)
10 
11 message(STATUS "Python Include: ${Python_INCLUDE_DIRS}")
12 
13 # execute_process将作为通过子进程执行一个或多个命令。最后,子进程返回值将保存到变量作为参数,传递给RESULT_VARIABLE,
14 # 而管道标准输出和标准错误的内容将被保存到变量作为参数传递给OUTPUT_VARIABLE和ERROR_VARIABLE。
15 # execute_process可以执行任何操作,并使用它们的结果来推断系统配置。本例中,用它来确保NumPy可用,然后获得模块版本。
16 execute_process(
17   COMMAND
18       ${PYTHON_EXECUTABLE} "-c" "import re, numpy; print(re.compile('/__init__.py.*').sub('',numpy.__file__))"
19   RESULT_VARIABLE _numpy_status
20   OUTPUT_VARIABLE _numpy_location
21   ERROR_QUIET
22   OUTPUT_STRIP_TRAILING_WHITESPACE
23   )
24 
25 if(NOT _numpy_status)
26     set(NumPy ${_numpy_location} CACHE STRING "Location of NumPy")
27 endif()
28 
29 execute_process(
30   COMMAND
31       ${PYTHON_EXECUTABLE} "-c" "import numpy; print(numpy.__version__)"
32   OUTPUT_VARIABLE _numpy_version
33   ERROR_QUIET
34   OUTPUT_STRIP_TRAILING_WHITESPACE
35   )
36 
37 # find_package_handle_standard_args提供了,用于处理与查找相关程序和库的标准工具。
38 # 所有必需的变量都设置为有效的文件路径(NumPy)后,发送到模块(NumPy_FOUND)。
39 # 它还将版本保存在可传递的版本变量(_numpy_version)中并打印:
40 # 目前的示例中,没有进一步使用这些变量。如果返回NumPy_FOUND为FALSE,则停止配置。
41 include(FindPackageHandleStandardArgs)
42 find_package_handle_standard_args(NumPy
43   FOUND_VAR NumPy_FOUND
44   REQUIRED_VARS NumPy
45   VERSION_VAR _numpy_version
46   )
47 
48 add_executable(${PROJECT_NAME} "")
49 
50 target_sources(${PROJECT_NAME}
51   PRIVATE
52       main.cpp
53   )
54 target_include_directories(${PROJECT_NAME}
55   PRIVATE
56       ${Python_INCLUDE_DIRS}
57   )
58 target_link_libraries(${PROJECT_NAME}
59   PRIVATE
60       ${Python_LIBRARIES}
61   )
62 
63 # 将use_numpy.py复制到build目录
64 # 选择使用add_custom_command,而不是file(COPY ...)来确保文件在每次更改时都会被复制,而不仅仅是第一次运行配置时
65 add_custom_command(
66   OUTPUT
67       ${CMAKE_CURRENT_BINARY_DIR}/use_numpy.py
68   COMMAND
69       ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/use_numpy.py
70       ${CMAKE_CURRENT_BINARY_DIR}/use_numpy.py
71   DEPENDS
72       ${CMAKE_CURRENT_SOURCE_DIR}/use_numpy.py
73   )
74 
75 # target_sources命令,它将依赖项添加到${CMAKE_CURRENT_BINARY_DIR}/use_numpy.py;这样做是为了确保构建目标,能够触发之前的命令。
76 target_sources(${PROJECT_NAME}
77   PRIVATE
78       ${CMAKE_CURRENT_BINARY_DIR}/use_numpy.py
79   )

 

posted @ 2022-08-06 14:22  禅元天道  阅读(300)  评论(0编辑  收藏  举报