已合并
refactor: protobuf生成切换工程函数 #1815
zhengfeiteng创建于 13 天前
refactor: protobuf生成切换工程函数 #1815
已合并
共 5 个文件变更+14-170
| @@ -1,9 +1,9 @@ | |||
| 1 | # ----------------------------------------------------------------------------------------------------------- | 1 | # ----------------------------------------------------------------------------------------------------------- |
| 2 | # Copyright (c) 2025 Huawei Technologies Co., Ltd. | 2 | # Copyright (c) 2025 Huawei Technologies Co., Ltd. |
| 3 | -# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of |
| 4 | # CANN Open Software License Agreement Version 2.0 (the "License"). | 4 | # CANN Open Software License Agreement Version 2.0 (the "License"). |
| 5 | # Please refer to the License for details. You may not use this file except in compliance with the License. | 5 | # Please refer to the License for details. You may not use this file except in compliance with the License. |
| 6 | -# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, |
| 7 | # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | 7 | # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. |
| 8 | # See LICENSE in the root of the software repository for the full text of the License. | 8 | # See LICENSE in the root of the software repository for the full text of the License. |
| 9 | # ----------------------------------------------------------------------------------------------------------- | 9 | # ----------------------------------------------------------------------------------------------------------- |
| @@ -18,7 +18,7 @@ set(METADEF_ATTR_GROUP_PROTO_LIST | |||
| 18 | "${METADEF_PROTO_DIR}/af_attr_group_base.proto" | 18 | "${METADEF_PROTO_DIR}/af_attr_group_base.proto" |
| 19 | ) | 19 | ) |
| 20 | 20 | ||
| 21 | -protobuf_generate(metadef_protos_af METADEF_PROTO_SRCS METADEF_PROTO_HDRS ${METADEF_PROTO_LIST} ${METADEF_ATTR_GROUP_PROTO_LIST} TARGET) | 21 | +generate_cann_protobuf(metadef_protos_af METADEF_PROTO_SRCS METADEF_PROTO_HDRS ${METADEF_PROTO_LIST} ${METADEF_ATTR_GROUP_PROTO_LIST} TARGET) |
| 22 | 22 | ||
| 23 | set(METADEF_GRAPH_PROTO_SRCS | 23 | set(METADEF_GRAPH_PROTO_SRCS |
| 24 | "${CMAKE_BINARY_DIR}/proto/metadef_protos_af/proto/af_ir.pb.cc" | 24 | "${CMAKE_BINARY_DIR}/proto/metadef_protos_af/proto/af_ir.pb.cc" |
| @@ -62,4 +62,3 @@ target_compile_options(metadef_attr_group_protos_obj_af PRIVATE | |||
| 62 | $<$<AND:$<STREQUAL:${TARGET_SYSTEM_NAME},Windows>,$<STREQUAL:${CMAKE_CONFIGURATION_TYPES},Debug>>:/MTd> | 62 | $<$<AND:$<STREQUAL:${TARGET_SYSTEM_NAME},Windows>,$<STREQUAL:${CMAKE_CONFIGURATION_TYPES},Debug>>:/MTd> |
| 63 | $<$<AND:$<STREQUAL:${TARGET_SYSTEM_NAME},Windows>,$<STREQUAL:${CMAKE_CONFIGURATION_TYPES},Release>>:/MT> | 63 | $<$<AND:$<STREQUAL:${TARGET_SYSTEM_NAME},Windows>,$<STREQUAL:${CMAKE_CONFIGURATION_TYPES},Release>>:/MT> |
| 64 | ) | 64 | ) |
| 65 | - | ||
| @@ -13,12 +13,12 @@ if(NOT PROJECT_SOURCE_DIR) | |||
| 13 | else() | 13 | else() |
| 14 | include(FetchContent) | 14 | include(FetchContent) |
| 15 | 15 | ||
| 16 | - set(CANN_CMAKE_TAG "master-026") | 16 | + set(CANN_CMAKE_TAG "master-052") |
| 17 | if(CANN_3RD_LIB_PATH AND EXISTS "${CANN_3RD_LIB_PATH}/cmake-${CANN_CMAKE_TAG}.tar.gz") | 17 | if(CANN_3RD_LIB_PATH AND EXISTS "${CANN_3RD_LIB_PATH}/cmake-${CANN_CMAKE_TAG}.tar.gz") |
| 18 | FetchContent_Declare( | 18 | FetchContent_Declare( |
| 19 | cann-cmake | 19 | cann-cmake |
| 20 | URL "${CANN_3RD_LIB_PATH}/cmake-${CANN_CMAKE_TAG}.tar.gz" | 20 | URL "${CANN_3RD_LIB_PATH}/cmake-${CANN_CMAKE_TAG}.tar.gz" |
| 21 | - URL_HASH SHA256=d453bc092448fca523e36cbfa57305a93303574984c9abe4c7574bb80092f96a | 21 | + URL_HASH SHA256=beea3a50b100da2e1d87a4c3bd649b6c7fc033b8314dda28b03a74af705993ed |
| 22 | ) | 22 | ) |
| 23 | else() | 23 | else() |
| 24 | FetchContent_Declare( | 24 | FetchContent_Declare( |
| @@ -34,4 +34,4 @@ if(NOT PROJECT_SOURCE_DIR) | |||
| 34 | endif() | 34 | endif() |
| 35 | include("${cann-cmake_SOURCE_DIR}/function/prepare.cmake") | 35 | include("${cann-cmake_SOURCE_DIR}/function/prepare.cmake") |
| 36 | endif() | 36 | endif() |
| 37 | -endif() | 37 | +endif() |
| @@ -1,9 +1,9 @@ | |||
| 1 | # ----------------------------------------------------------------------------------------------------------- | 1 | # ----------------------------------------------------------------------------------------------------------- |
| 2 | # Copyright (c) 2025 Huawei Technologies Co., Ltd. | 2 | # Copyright (c) 2025 Huawei Technologies Co., Ltd. |
| 3 | -# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | 3 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of |
| 4 | # CANN Open Software License Agreement Version 2.0 (the "License"). | 4 | # CANN Open Software License Agreement Version 2.0 (the "License"). |
| 5 | # Please refer to the License for details. You may not use this file except in compliance with the License. | 5 | # Please refer to the License for details. You may not use this file except in compliance with the License. |
| 6 | -# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | 6 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, |
| 7 | # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | 7 | # INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. |
| 8 | # See LICENSE in the root of the software repository for the full text of the License. | 8 | # See LICENSE in the root of the software repository for the full text of the License. |
| 9 | # ----------------------------------------------------------------------------------------------------------- | 9 | # ----------------------------------------------------------------------------------------------------------- |
| @@ -38,158 +38,3 @@ function(find_module module name) | |||
| 38 | IMPORTED_LOCATION ${${module}_LIBRARY_DIR} | 38 | IMPORTED_LOCATION ${${module}_LIBRARY_DIR} |
| 39 | ) | 39 | ) |
| 40 | endfunction() | 40 | endfunction() |
| 41 | - | ||
| 42 | -function(protobuf_generate comp c_var h_var) | ||
| 43 | - if (NOT ARGN) | ||
| 44 | - message(SEND_ERROR "Error: protobuf_generate() called without any proto files") | ||
| 45 | - return() | ||
| 46 | - endif () | ||
| 47 | - set(${c_var}) | ||
| 48 | - set(${h_var}) | ||
| 49 | - set(_add_target FALSE) | ||
| 50 | - | ||
| 51 | - set(extra_option "") | ||
| 52 | - foreach (arg ${ARGN}) | ||
| 53 | - if ("${arg}" MATCHES "--proto_path") | ||
| 54 | - set(extra_option ${arg}) | ||
| 55 | - endif () | ||
| 56 | - if ("${arg}" STREQUAL "TARGET") | ||
| 57 | - set(_add_target TRUE) | ||
| 58 | - endif () | ||
| 59 | - endforeach () | ||
| 60 | - | ||
| 61 | - foreach (file ${ARGN}) | ||
| 62 | - if ("${file}" MATCHES "--proto_path") | ||
| 63 | - continue() | ||
| 64 | - endif () | ||
| 65 | - if ("${file}" STREQUAL "TARGET") | ||
| 66 | - continue() | ||
| 67 | - endif () | ||
| 68 | - | ||
| 69 | - get_filename_component(abs_file ${file} ABSOLUTE) | ||
| 70 | - get_filename_component(file_name ${file} NAME_WE) | ||
| 71 | - get_filename_component(file_dir ${abs_file} PATH) | ||
| 72 | - get_filename_component(parent_subdir ${file_dir} NAME) | ||
| 73 | - | ||
| 74 | - if ("${parent_subdir}" STREQUAL "proto") | ||
| 75 | - set(proto_output_path ${CMAKE_BINARY_DIR}/proto/${comp}/proto) | ||
| 76 | - else () | ||
| 77 | - set(proto_output_path ${CMAKE_BINARY_DIR}/proto/${comp}/proto/${parent_subdir}) | ||
| 78 | - endif () | ||
| 79 | - list(APPEND ${c_var} "${proto_output_path}/${file_name}.pb.cc") | ||
| 80 | - list(APPEND ${h_var} "${proto_output_path}/${file_name}.pb.h") | ||
| 81 | - | ||
| 82 | - add_custom_command( | ||
| 83 | - OUTPUT "${proto_output_path}/${file_name}.pb.cc" "${proto_output_path}/${file_name}.pb.h" | ||
| 84 | - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} | ||
| 85 | - COMMAND ${CMAKE_COMMAND} -E make_directory "${proto_output_path}" | ||
| 86 | - COMMAND ${CMAKE_COMMAND} -E echo "generate proto cpp_out ${comp} by ${abs_file}" | ||
| 87 | - COMMAND $<TARGET_FILE:host_protoc> -I${file_dir} ${extra_option} --cpp_out=${proto_output_path} ${abs_file} | ||
| 88 | - DEPENDS ${abs_file} | ||
| 89 | - COMMENT "Running C++ protocol buffer compiler on ${file}" VERBATIM ) | ||
| 90 | - endforeach () | ||
| 91 | - | ||
| 92 | - if (_add_target) | ||
| 93 | - add_custom_target( | ||
| 94 | - ${comp} DEPENDS ${${c_var}} ${${h_var}} | ||
| 95 | - ) | ||
| 96 | - endif () | ||
| 97 | - | ||
| 98 | - set_source_files_properties(${${c_var}} ${${h_var}} PROPERTIES GENERATED TRUE) | ||
| 99 | - set(${c_var} ${${c_var}} PARENT_SCOPE) | ||
| 100 | - set(${h_var} ${${h_var}} PARENT_SCOPE) | ||
| 101 | -endfunction() | ||
| 102 | - | ||
| 103 | -function(protobuf_generate_grpc comp c_var h_var) | ||
| 104 | - if (NOT ARGN) | ||
| 105 | - MESSAGE(SEND_ERROR "Error:protobuf_generate_grpc() called without any proto files") | ||
| 106 | - return() | ||
| 107 | - endif () | ||
| 108 | - set(${c_var}) | ||
| 109 | - set(${h_var}) | ||
| 110 | - | ||
| 111 | - set(extra_option "") | ||
| 112 | - foreach (arg ${ARGN}) | ||
| 113 | - if ("${arg}" MATCHES "--proto_path") | ||
| 114 | - set(extra_option ${arg}) | ||
| 115 | - endif () | ||
| 116 | - endforeach () | ||
| 117 | - | ||
| 118 | - foreach (file ${ARGN}) | ||
| 119 | - if ("${file}" MATCHES "--proto_path") | ||
| 120 | - continue() | ||
| 121 | - endif () | ||
| 122 | - get_filename_component(abs_file ${file} ABSOLUTE) | ||
| 123 | - get_filename_component(file_name ${file} NAME_WE) | ||
| 124 | - get_filename_component(file_dir ${abs_file} PATH) | ||
| 125 | - get_filename_component(parent_subdir ${file_dir} NAME) | ||
| 126 | - if ("${parent_subdir}" STREQUAL "proto") | ||
| 127 | - set(proto_output_path ${CMAKE_BINARY_DIR}/proto_grpc/${comp}/proto) | ||
| 128 | - else () | ||
| 129 | - set(proto_output_path ${CMAKE_BINARY_DIR}/proto_grpc/${comp}/proto_grpc/${parent_subdir}) | ||
| 130 | - endif () | ||
| 131 | - list(APPEND ${c_var} "${proto_output_path}/${file_name}.pb.cc") | ||
| 132 | - list(APPEND ${c_var} "${proto_output_path}/${file_name}.grpc.pb.cc") | ||
| 133 | - list(APPEND ${h_var} "${proto_output_path}/${file_name}.pb.h") | ||
| 134 | - list(APPEND ${h_var} "${proto_output_path}/${file_name}.grpc.pb.h") | ||
| 135 | - | ||
| 136 | - add_custom_command( | ||
| 137 | - OUTPUT "${proto_output_path}/${file_name}.pb.cc" "${proto_output_path}/${file_name}.pb.h" | ||
| 138 | - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} | ||
| 139 | - COMMAND ${CMAKE_COMMAND} -E make_directory "${proto_output_path}" | ||
| 140 | - COMMAND $<TARGET_FILE:host_protoc> -I${file_dir} ${extra_option} --cpp_out=${proto_output_path} ${abs_file} | ||
| 141 | - COMMAND $<TARGET_FILE:host_protoc> -I${file_dir} ${extra_option} --grpc_out=${proto_output_path} --plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN_PROGRAM} ${abs_file} | ||
| 142 | - DEPENDS ${abs_file} | ||
| 143 | - COMMENT "Running C++ protocol buffer complier on ${file}" VERBATIM) | ||
| 144 | - endforeach () | ||
| 145 | - | ||
| 146 | - set_source_files_properties(${${c_var}} ${${h_var}} PROPERTIES GENERATED TRUE) | ||
| 147 | - set(${c_var} ${${c_var}} PARENT_SCOPE) | ||
| 148 | - set(${h_var} ${${h_var}} PARENT_SCOPE) | ||
| 149 | -endfunction() | ||
| 150 | - | ||
| 151 | -function(protobuf_generate_py comp py_var) | ||
| 152 | - if (NOT ARGN) | ||
| 153 | - message(SEND_ERROR "Error: protobuf_generate_py() called without any proto files") | ||
| 154 | - return() | ||
| 155 | - endif () | ||
| 156 | - set(${py_var}) | ||
| 157 | - set(_add_target FALSE) | ||
| 158 | - | ||
| 159 | - foreach (file ${ARGN}) | ||
| 160 | - if ("${file}" STREQUAL "TARGET") | ||
| 161 | - set(_add_target TRUE) | ||
| 162 | - continue() | ||
| 163 | - endif () | ||
| 164 | - | ||
| 165 | - get_filename_component(abs_file ${file} ABSOLUTE) | ||
| 166 | - get_filename_component(file_name ${file} NAME_WE) | ||
| 167 | - get_filename_component(file_dir ${abs_file} PATH) | ||
| 168 | - get_filename_component(parent_subdir ${file_dir} NAME) | ||
| 169 | - | ||
| 170 | - if ("${parent_subdir}" STREQUAL "proto") | ||
| 171 | - set(proto_output_path ${CMAKE_BINARY_DIR}/proto/${comp}/proto) | ||
| 172 | - else () | ||
| 173 | - set(proto_output_path ${CMAKE_BINARY_DIR}/proto/${comp}/proto/${parent_subdir}) | ||
| 174 | - endif () | ||
| 175 | - list(APPEND ${py_var} "${proto_output_path}/${file_name}_pb2.py") | ||
| 176 | - | ||
| 177 | - add_custom_command( | ||
| 178 | - OUTPUT "${proto_output_path}/${file_name}_pb2.py" | ||
| 179 | - WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} | ||
| 180 | - COMMAND ${CMAKE_COMMAND} -E make_directory "${proto_output_path}" | ||
| 181 | - COMMAND ${CMAKE_COMMAND} -E echo "generate proto cpp_out ${comp} by ${abs_file}" | ||
| 182 | - COMMAND $<TARGET_FILE:host_protoc> -I${file_dir} --python_out=${proto_output_path} ${abs_file} | ||
| 183 | - DEPENDS ${abs_file} | ||
| 184 | - COMMENT "Running PYTHON protocol buffer compiler on ${file}" VERBATIM ) | ||
| 185 | - endforeach () | ||
| 186 | - | ||
| 187 | - if (_add_target) | ||
| 188 | - add_custom_target( | ||
| 189 | - ${comp} DEPENDS ${${py_var}} | ||
| 190 | - ) | ||
| 191 | - endif () | ||
| 192 | - | ||
| 193 | - set_source_files_properties(${${py_var}} PROPERTIES GENERATED TRUE) | ||
| 194 | - set(${py_var} ${${py_var}} PARENT_SCOPE) | ||
| 195 | -endfunction() | ||
| @@ -45,7 +45,7 @@ If your compilation environment cannot access the network, download the source c | |||
| 45 | | Open-Source Software | Version | Download Link | | 45 | | Open-Source Software | Version | Download Link | |
| 46 | |---|---|---| | 46 | |---|---|---| |
| 47 | | makeself | 2.5.0 | [makeself-release-2.5.0-patch1.tar.gz](https://gitcode.com/cann-src-third-party/makeself/releases/download/release-2.5.0-patch1.0/makeself-release-2.5.0-patch1.tar.gz) | | 47 | | makeself | 2.5.0 | [makeself-release-2.5.0-patch1.tar.gz](https://gitcode.com/cann-src-third-party/makeself/releases/download/release-2.5.0-patch1.0/makeself-release-2.5.0-patch1.tar.gz) | |
| 48 | - | cann-cmake | master-002 | [cmake-master-002.tar.gz](https://cann-3rd.obs.cn-north-4.myhuaweicloud.com/cmake/cmake-master-002.tar.gz) | | 48 | + | cann-cmake | master-052 | [cmake-master-052.tar.gz](https://cann-3rd.obs.cn-north-4.myhuaweicloud.com/cmake/cmake-master-052.tar.gz) | |
| 49 | 49 | ||
| 50 | 3. Upload the source code and third-party open-source software to the offline compilation environment. Extract the source code. | 50 | 3. Upload the source code and third-party open-source software to the offline compilation environment. Extract the source code. |
| 51 | 51 | ||
| @@ -61,7 +61,7 @@ If your compilation environment cannot access the network, download the source c | |||
| 61 | mkdir open_source | 61 | mkdir open_source |
| 62 | # Move third-party open-source software packages to open_source directory | 62 | # Move third-party open-source software packages to open_source directory |
| 63 | mv makeself-release-2.5.0-patch1.tar.gz open_source/ | 63 | mv makeself-release-2.5.0-patch1.tar.gz open_source/ |
| 64 | - mv cmake-master-002.tar.gz open_source/ | 64 | + mv cmake-master-052.tar.gz open_source/ |
| 65 | # Extract third-party open-source software packages and rename (cann-cmake package remains unchanged) | 65 | # Extract third-party open-source software packages and rename (cann-cmake package remains unchanged) |
| 66 | cd open_source | 66 | cd open_source |
| 67 | tar -zxvf makeself-release-2.5.0-patch1.tar.gz && mv makeself-release-2.5.0 makeself | 67 | tar -zxvf makeself-release-2.5.0-patch1.tar.gz && mv makeself-release-2.5.0 makeself |
| @@ -79,7 +79,7 @@ If your compilation environment cannot access the network, download the source c | |||
| 79 | │ ├── Makefile | 79 | │ ├── Makefile |
| 80 | │ ├── README.md | 80 | │ ├── README.md |
| 81 | │ └── ... | 81 | │ └── ... |
| 82 | - │ └── cmake-master-002.tar.gz | 82 | + │ └── cmake-master-052.tar.gz |
| 83 | ``` | 83 | ``` |
| 84 | 84 | ||
| 85 | ### 4.2 Install Dependencies | 85 | ### 4.2 Install Dependencies |
| @@ -45,7 +45,7 @@ git clone https://gitcode.com/cann/graph-autofusion.git | |||
| 45 | | 开源软件 | 版本 | 下载地址 | | 45 | | 开源软件 | 版本 | 下载地址 | |
| 46 | |---|---|---| | 46 | |---|---|---| |
| 47 | | makeself | 2.5.0 | [makeself-release-2.5.0-patch1.tar.gz](https://gitcode.com/cann-src-third-party/makeself/releases/download/release-2.5.0-patch1.0/makeself-release-2.5.0-patch1.tar.gz) | | 47 | | makeself | 2.5.0 | [makeself-release-2.5.0-patch1.tar.gz](https://gitcode.com/cann-src-third-party/makeself/releases/download/release-2.5.0-patch1.0/makeself-release-2.5.0-patch1.tar.gz) | |
| 48 | - | cann-cmake | master-002 | [cmake-master-002.tar.gz](https://cann-3rd.obs.cn-north-4.myhuaweicloud.com/cmake/cmake-master-002.tar.gz) | | 48 | + | cann-cmake | master-052 | [cmake-master-052.tar.gz](https://cann-3rd.obs.cn-north-4.myhuaweicloud.com/cmake/cmake-master-052.tar.gz) | |
| 49 | 49 | ||
| 50 | 3. 将源码及第三方开源软件上传到离线编译环境中,解压缩源码。 | 50 | 3. 将源码及第三方开源软件上传到离线编译环境中,解压缩源码。 |
| 51 | 51 | ||
| @@ -61,7 +61,7 @@ git clone https://gitcode.com/cann/graph-autofusion.git | |||
| 61 | mkdir open_source | 61 | mkdir open_source |
| 62 | # 将第三方开源软件压缩包放到open_source目录下 | 62 | # 将第三方开源软件压缩包放到open_source目录下 |
| 63 | mv makeself-release-2.5.0-patch1.tar.gz open_source/ | 63 | mv makeself-release-2.5.0-patch1.tar.gz open_source/ |
| 64 | - mv cmake-master-002.tar.gz open_source/ | 64 | + mv cmake-master-052.tar.gz open_source/ |
| 65 | # 解压缩第三方开源软件压缩包并改名(cann-cmake压缩包保持不变) | 65 | # 解压缩第三方开源软件压缩包并改名(cann-cmake压缩包保持不变) |
| 66 | cd open_source | 66 | cd open_source |
| 67 | tar -zxvf makeself-release-2.5.0-patch1.tar.gz && mv makeself-release-2.5.0 makeself | 67 | tar -zxvf makeself-release-2.5.0-patch1.tar.gz && mv makeself-release-2.5.0 makeself |
| @@ -79,7 +79,7 @@ git clone https://gitcode.com/cann/graph-autofusion.git | |||
| 79 | │ ├── Makefile | 79 | │ ├── Makefile |
| 80 | │ ├── README.md | 80 | │ ├── README.md |
| 81 | │ └── ... | 81 | │ └── ... |
| 82 | - │ └── cmake-master-002.tar.gz | 82 | + │ └── cmake-master-052.tar.gz |
| 83 | ``` | 83 | ``` |
| 84 | 84 | ||
| 85 | ### 4.2 安装依赖 | 85 | ### 4.2 安装依赖 |