Committed by
GitHub
Refactor cmake (#24)
* Refactor cmake * small fixes
正在显示
8 个修改的文件
包含
87 行增加
和
987 行删除
cmake/Modules/FetchContent.cmake
已删除
100644 → 0
| 1 | -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying | ||
| 2 | -# file Copyright.txt or https://cmake.org/licensing for details. | ||
| 3 | - | ||
| 4 | -#[=======================================================================[.rst: | ||
| 5 | -FetchContent | ||
| 6 | ------------------- | ||
| 7 | - | ||
| 8 | -.. only:: html | ||
| 9 | - | ||
| 10 | - .. contents:: | ||
| 11 | - | ||
| 12 | -Overview | ||
| 13 | -^^^^^^^^ | ||
| 14 | - | ||
| 15 | -This module enables populating content at configure time via any method | ||
| 16 | -supported by the :module:`ExternalProject` module. Whereas | ||
| 17 | -:command:`ExternalProject_Add` downloads at build time, the | ||
| 18 | -``FetchContent`` module makes content available immediately, allowing the | ||
| 19 | -configure step to use the content in commands like :command:`add_subdirectory`, | ||
| 20 | -:command:`include` or :command:`file` operations. | ||
| 21 | - | ||
| 22 | -Content population details would normally be defined separately from the | ||
| 23 | -command that performs the actual population. Projects should also | ||
| 24 | -check whether the content has already been populated somewhere else in the | ||
| 25 | -project hierarchy. Typical usage would look something like this: | ||
| 26 | - | ||
| 27 | -.. code-block:: cmake | ||
| 28 | - | ||
| 29 | - FetchContent_Declare( | ||
| 30 | - googletest | ||
| 31 | - GIT_REPOSITORY https://github.com/google/googletest.git | ||
| 32 | - GIT_TAG release-1.8.0 | ||
| 33 | - ) | ||
| 34 | - | ||
| 35 | - FetchContent_GetProperties(googletest) | ||
| 36 | - if(NOT googletest_POPULATED) | ||
| 37 | - FetchContent_Populate(googletest) | ||
| 38 | - add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) | ||
| 39 | - endif() | ||
| 40 | - | ||
| 41 | -When using the above pattern with a hierarchical project arrangement, | ||
| 42 | -projects at higher levels in the hierarchy are able to define or override | ||
| 43 | -the population details of content specified anywhere lower in the project | ||
| 44 | -hierarchy. The ability to detect whether content has already been | ||
| 45 | -populated ensures that even if multiple child projects want certain content | ||
| 46 | -to be available, the first one to populate it wins. The other child project | ||
| 47 | -can simply make use of the already available content instead of repeating | ||
| 48 | -the population for itself. See the | ||
| 49 | -:ref:`Examples <fetch-content-examples>` section which demonstrates | ||
| 50 | -this scenario. | ||
| 51 | - | ||
| 52 | -The ``FetchContent`` module also supports defining and populating | ||
| 53 | -content in a single call, with no check for whether the content has been | ||
| 54 | -populated elsewhere in the project already. This is a more low level | ||
| 55 | -operation and would not normally be the way the module is used, but it is | ||
| 56 | -sometimes useful as part of implementing some higher level feature or to | ||
| 57 | -populate some content in CMake's script mode. | ||
| 58 | - | ||
| 59 | - | ||
| 60 | -Declaring Content Details | ||
| 61 | -^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 62 | - | ||
| 63 | -.. command:: FetchContent_Declare | ||
| 64 | - | ||
| 65 | - .. code-block:: cmake | ||
| 66 | - | ||
| 67 | - FetchContent_Declare(<name> <contentOptions>...) | ||
| 68 | - | ||
| 69 | - The ``FetchContent_Declare()`` function records the options that describe | ||
| 70 | - how to populate the specified content, but if such details have already | ||
| 71 | - been recorded earlier in this project (regardless of where in the project | ||
| 72 | - hierarchy), this and all later calls for the same content ``<name>`` are | ||
| 73 | - ignored. This "first to record, wins" approach is what allows hierarchical | ||
| 74 | - projects to have parent projects override content details of child projects. | ||
| 75 | - | ||
| 76 | - The content ``<name>`` can be any string without spaces, but good practice | ||
| 77 | - would be to use only letters, numbers and underscores. The name will be | ||
| 78 | - treated case-insensitively and it should be obvious for the content it | ||
| 79 | - represents, often being the name of the child project or the value given | ||
| 80 | - to its top level :command:`project` command (if it is a CMake project). | ||
| 81 | - For well-known public projects, the name should generally be the official | ||
| 82 | - name of the project. Choosing an unusual name makes it unlikely that other | ||
| 83 | - projects needing that same content will use the same name, leading to | ||
| 84 | - the content being populated multiple times. | ||
| 85 | - | ||
| 86 | - The ``<contentOptions>`` can be any of the download or update/patch options | ||
| 87 | - that the :command:`ExternalProject_Add` command understands. The configure, | ||
| 88 | - build, install and test steps are explicitly disabled and therefore options | ||
| 89 | - related to them will be ignored. In most cases, ``<contentOptions>`` will | ||
| 90 | - just be a couple of options defining the download method and method-specific | ||
| 91 | - details like a commit tag or archive hash. For example: | ||
| 92 | - | ||
| 93 | - .. code-block:: cmake | ||
| 94 | - | ||
| 95 | - FetchContent_Declare( | ||
| 96 | - googletest | ||
| 97 | - GIT_REPOSITORY https://github.com/google/googletest.git | ||
| 98 | - GIT_TAG release-1.8.0 | ||
| 99 | - ) | ||
| 100 | - | ||
| 101 | - FetchContent_Declare( | ||
| 102 | - myCompanyIcons | ||
| 103 | - URL https://intranet.mycompany.com/assets/iconset_1.12.tar.gz | ||
| 104 | - URL_HASH 5588a7b18261c20068beabfb4f530b87 | ||
| 105 | - ) | ||
| 106 | - | ||
| 107 | - FetchContent_Declare( | ||
| 108 | - myCompanyCertificates | ||
| 109 | - SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs | ||
| 110 | - SVN_REVISION -r12345 | ||
| 111 | - ) | ||
| 112 | - | ||
| 113 | -Populating The Content | ||
| 114 | -^^^^^^^^^^^^^^^^^^^^^^ | ||
| 115 | - | ||
| 116 | -.. command:: FetchContent_Populate | ||
| 117 | - | ||
| 118 | - .. code-block:: cmake | ||
| 119 | - | ||
| 120 | - FetchContent_Populate( <name> ) | ||
| 121 | - | ||
| 122 | - In most cases, the only argument given to ``FetchContent_Populate()`` is the | ||
| 123 | - ``<name>``. When used this way, the command assumes the content details have | ||
| 124 | - been recorded by an earlier call to :command:`FetchContent_Declare`. The | ||
| 125 | - details are stored in a global property, so they are unaffected by things | ||
| 126 | - like variable or directory scope. Therefore, it doesn't matter where in the | ||
| 127 | - project the details were previously declared, as long as they have been | ||
| 128 | - declared before the call to ``FetchContent_Populate()``. Those saved details | ||
| 129 | - are then used to construct a call to :command:`ExternalProject_Add` in a | ||
| 130 | - private sub-build to perform the content population immediately. The | ||
| 131 | - implementation of ``ExternalProject_Add()`` ensures that if the content has | ||
| 132 | - already been populated in a previous CMake run, that content will be reused | ||
| 133 | - rather than repopulating them again. For the common case where population | ||
| 134 | - involves downloading content, the cost of the download is only paid once. | ||
| 135 | - | ||
| 136 | - An internal global property records when a particular content population | ||
| 137 | - request has been processed. If ``FetchContent_Populate()`` is called more | ||
| 138 | - than once for the same content name within a configure run, the second call | ||
| 139 | - will halt with an error. Projects can and should check whether content | ||
| 140 | - population has already been processed with the | ||
| 141 | - :command:`FetchContent_GetProperties` command before calling | ||
| 142 | - ``FetchContent_Populate()``. | ||
| 143 | - | ||
| 144 | - ``FetchContent_Populate()`` will set three variables in the scope of the | ||
| 145 | - caller; ``<lcName>_POPULATED``, ``<lcName>_SOURCE_DIR`` and | ||
| 146 | - ``<lcName>_BINARY_DIR``, where ``<lcName>`` is the lowercased ``<name>``. | ||
| 147 | - ``<lcName>_POPULATED`` will always be set to ``True`` by the call. | ||
| 148 | - ``<lcName>_SOURCE_DIR`` is the location where the | ||
| 149 | - content can be found upon return (it will have already been populated), while | ||
| 150 | - ``<lcName>_BINARY_DIR`` is a directory intended for use as a corresponding | ||
| 151 | - build directory. The main use case for the two directory variables is to | ||
| 152 | - call :command:`add_subdirectory` immediately after population, i.e.: | ||
| 153 | - | ||
| 154 | - .. code-block:: cmake | ||
| 155 | - | ||
| 156 | - FetchContent_Populate(FooBar ...) | ||
| 157 | - add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR}) | ||
| 158 | - | ||
| 159 | - The values of the three variables can also be retrieved from anywhere in the | ||
| 160 | - project hierarchy using the :command:`FetchContent_GetProperties` command. | ||
| 161 | - | ||
| 162 | - A number of cache variables influence the behavior of all content population | ||
| 163 | - performed using details saved from a :command:`FetchContent_Declare` call: | ||
| 164 | - | ||
| 165 | - ``FETCHCONTENT_BASE_DIR`` | ||
| 166 | - In most cases, the saved details do not specify any options relating to the | ||
| 167 | - directories to use for the internal sub-build, final source and build areas. | ||
| 168 | - It is generally best to leave these decisions up to the ``FetchContent`` | ||
| 169 | - module to handle on the project's behalf. The ``FETCHCONTENT_BASE_DIR`` | ||
| 170 | - cache variable controls the point under which all content population | ||
| 171 | - directories are collected, but in most cases developers would not need to | ||
| 172 | - change this. The default location is ``${CMAKE_BINARY_DIR}/_deps``, but if | ||
| 173 | - developers change this value, they should aim to keep the path short and | ||
| 174 | - just below the top level of the build tree to avoid running into path | ||
| 175 | - length problems on Windows. | ||
| 176 | - | ||
| 177 | - ``FETCHCONTENT_QUIET`` | ||
| 178 | - The logging output during population can be quite verbose, making the | ||
| 179 | - configure stage quite noisy. This cache option (``ON`` by default) hides | ||
| 180 | - all population output unless an error is encountered. If experiencing | ||
| 181 | - problems with hung downloads, temporarily switching this option off may | ||
| 182 | - help diagnose which content population is causing the issue. | ||
| 183 | - | ||
| 184 | - ``FETCHCONTENT_FULLY_DISCONNECTED`` | ||
| 185 | - When this option is enabled, no attempt is made to download or update | ||
| 186 | - any content. It is assumed that all content has already been populated in | ||
| 187 | - a previous run or the source directories have been pointed at existing | ||
| 188 | - contents the developer has provided manually (using options described | ||
| 189 | - further below). When the developer knows that no changes have been made to | ||
| 190 | - any content details, turning this option ``ON`` can significantly speed up | ||
| 191 | - the configure stage. It is ``OFF`` by default. | ||
| 192 | - | ||
| 193 | - ``FETCHCONTENT_UPDATES_DISCONNECTED`` | ||
| 194 | - This is a less severe download/update control compared to | ||
| 195 | - ``FETCHCONTENT_FULLY_DISCONNECTED``. Instead of bypassing all download and | ||
| 196 | - update logic, the ``FETCHCONTENT_UPDATES_DISCONNECTED`` only disables the | ||
| 197 | - update stage. Therefore, if content has not been downloaded previously, | ||
| 198 | - it will still be downloaded when this option is enabled. This can speed up | ||
| 199 | - the configure stage, but not as much as | ||
| 200 | - ``FETCHCONTENT_FULLY_DISCONNECTED``. It is ``OFF`` by default. | ||
| 201 | - | ||
| 202 | - In addition to the above cache variables, the following cache variables are | ||
| 203 | - also defined for each content name (``<ucName>`` is the uppercased value of | ||
| 204 | - ``<name>``): | ||
| 205 | - | ||
| 206 | - ``FETCHCONTENT_SOURCE_DIR_<ucName>`` | ||
| 207 | - If this is set, no download or update steps are performed for the specified | ||
| 208 | - content and the ``<lcName>_SOURCE_DIR`` variable returned to the caller is | ||
| 209 | - pointed at this location. This gives developers a way to have a separate | ||
| 210 | - checkout of the content that they can modify freely without interference | ||
| 211 | - from the build. The build simply uses that existing source, but it still | ||
| 212 | - defines ``<lcName>_BINARY_DIR`` to point inside its own build area. | ||
| 213 | - Developers are strongly encouraged to use this mechanism rather than | ||
| 214 | - editing the sources populated in the default location, as changes to | ||
| 215 | - sources in the default location can be lost when content population details | ||
| 216 | - are changed by the project. | ||
| 217 | - | ||
| 218 | - ``FETCHCONTENT_UPDATES_DISCONNECTED_<ucName>`` | ||
| 219 | - This is the per-content equivalent of | ||
| 220 | - ``FETCHCONTENT_UPDATES_DISCONNECTED``. If the global option or this option | ||
| 221 | - is ``ON``, then updates will be disabled for the named content. | ||
| 222 | - Disabling updates for individual content can be useful for content whose | ||
| 223 | - details rarely change, while still leaving other frequently changing | ||
| 224 | - content with updates enabled. | ||
| 225 | - | ||
| 226 | - | ||
| 227 | - The ``FetchContent_Populate()`` command also supports a syntax allowing the | ||
| 228 | - content details to be specified directly rather than using any saved | ||
| 229 | - details. This is more low-level and use of this form is generally to be | ||
| 230 | - avoided in favour of using saved content details as outlined above. | ||
| 231 | - Nevertheless, in certain situations it can be useful to invoke the content | ||
| 232 | - population as an isolated operation (typically as part of implementing some | ||
| 233 | - other higher level feature or when using CMake in script mode): | ||
| 234 | - | ||
| 235 | - .. code-block:: cmake | ||
| 236 | - | ||
| 237 | - FetchContent_Populate( <name> | ||
| 238 | - [QUIET] | ||
| 239 | - [SUBBUILD_DIR <subBuildDir>] | ||
| 240 | - [SOURCE_DIR <srcDir>] | ||
| 241 | - [BINARY_DIR <binDir>] | ||
| 242 | - ... | ||
| 243 | - ) | ||
| 244 | - | ||
| 245 | - This form has a number of key differences to that where only ``<name>`` is | ||
| 246 | - provided: | ||
| 247 | - | ||
| 248 | - - All required population details are assumed to have been provided directly | ||
| 249 | - in the call to ``FetchContent_Populate()``. Any saved details for | ||
| 250 | - ``<name>`` are ignored. | ||
| 251 | - - No check is made for whether content for ``<name>`` has already been | ||
| 252 | - populated. | ||
| 253 | - - No global property is set to record that the population has occurred. | ||
| 254 | - - No global properties record the source or binary directories used for the | ||
| 255 | - populated content. | ||
| 256 | - - The ``FETCHCONTENT_FULLY_DISCONNECTED`` and | ||
| 257 | - ``FETCHCONTENT_UPDATES_DISCONNECTED`` cache variables are ignored. | ||
| 258 | - | ||
| 259 | - The ``<lcName>_SOURCE_DIR`` and ``<lcName>_BINARY_DIR`` variables are still | ||
| 260 | - returned to the caller, but since these locations are not stored as global | ||
| 261 | - properties when this form is used, they are only available to the calling | ||
| 262 | - scope and below rather than the entire project hierarchy. No | ||
| 263 | - ``<lcName>_POPULATED`` variable is set in the caller's scope with this form. | ||
| 264 | - | ||
| 265 | - The supported options for ``FetchContent_Populate()`` are the same as those | ||
| 266 | - for :command:`FetchContent_Declare()`. Those few options shown just | ||
| 267 | - above are either specific to ``FetchContent_Populate()`` or their behavior is | ||
| 268 | - slightly modified from how :command:`ExternalProject_Add` treats them. | ||
| 269 | - | ||
| 270 | - ``QUIET`` | ||
| 271 | - The ``QUIET`` option can be given to hide the output associated with | ||
| 272 | - populating the specified content. If the population fails, the output will | ||
| 273 | - be shown regardless of whether this option was given or not so that the | ||
| 274 | - cause of the failure can be diagnosed. The global ``FETCHCONTENT_QUIET`` | ||
| 275 | - cache variable has no effect on ``FetchContent_Populate()`` calls where the | ||
| 276 | - content details are provided directly. | ||
| 277 | - | ||
| 278 | - ``SUBBUILD_DIR`` | ||
| 279 | - The ``SUBBUILD_DIR`` argument can be provided to change the location of the | ||
| 280 | - sub-build created to perform the population. The default value is | ||
| 281 | - ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-subbuild`` and it would be unusual | ||
| 282 | - to need to override this default. If a relative path is specified, it will | ||
| 283 | - be interpreted as relative to :variable:`CMAKE_CURRENT_BINARY_DIR`. | ||
| 284 | - | ||
| 285 | - ``SOURCE_DIR``, ``BINARY_DIR`` | ||
| 286 | - The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by | ||
| 287 | - :command:`ExternalProject_Add`, but different default values are used by | ||
| 288 | - ``FetchContent_Populate()``. ``SOURCE_DIR`` defaults to | ||
| 289 | - ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-src`` and ``BINARY_DIR`` defaults to | ||
| 290 | - ``${CMAKE_CURRENT_BINARY_DIR}/<lcName>-build``. If a relative path is | ||
| 291 | - specified, it will be interpreted as relative to | ||
| 292 | - :variable:`CMAKE_CURRENT_BINARY_DIR`. | ||
| 293 | - | ||
| 294 | - In addition to the above explicit options, any other unrecognized options are | ||
| 295 | - passed through unmodified to :command:`ExternalProject_Add` to perform the | ||
| 296 | - download, patch and update steps. The following options are explicitly | ||
| 297 | - prohibited (they are disabled by the ``FetchContent_Populate()`` command): | ||
| 298 | - | ||
| 299 | - - ``CONFIGURE_COMMAND`` | ||
| 300 | - - ``BUILD_COMMAND`` | ||
| 301 | - - ``INSTALL_COMMAND`` | ||
| 302 | - - ``TEST_COMMAND`` | ||
| 303 | - | ||
| 304 | - If using ``FetchContent_Populate()`` within CMake's script mode, be aware | ||
| 305 | - that the implementation sets up a sub-build which therefore requires a CMake | ||
| 306 | - generator and build tool to be available. If these cannot be found by | ||
| 307 | - default, then the :variable:`CMAKE_GENERATOR` and/or | ||
| 308 | - :variable:`CMAKE_MAKE_PROGRAM` variables will need to be set appropriately | ||
| 309 | - on the command line invoking the script. | ||
| 310 | - | ||
| 311 | - | ||
| 312 | -Retrieve Population Properties | ||
| 313 | -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 314 | - | ||
| 315 | -.. command:: FetchContent_GetProperties | ||
| 316 | - | ||
| 317 | - When using saved content details, a call to :command:`FetchContent_Populate` | ||
| 318 | - records information in global properties which can be queried at any time. | ||
| 319 | - This information includes the source and binary directories associated with | ||
| 320 | - the content and also whether or not the content population has been processed | ||
| 321 | - during the current configure run. | ||
| 322 | - | ||
| 323 | - .. code-block:: cmake | ||
| 324 | - | ||
| 325 | - FetchContent_GetProperties( <name> | ||
| 326 | - [SOURCE_DIR <srcDirVar>] | ||
| 327 | - [BINARY_DIR <binDirVar>] | ||
| 328 | - [POPULATED <doneVar>] | ||
| 329 | - ) | ||
| 330 | - | ||
| 331 | - The ``SOURCE_DIR``, ``BINARY_DIR`` and ``POPULATED`` options can be used to | ||
| 332 | - specify which properties should be retrieved. Each option accepts a value | ||
| 333 | - which is the name of the variable in which to store that property. Most of | ||
| 334 | - the time though, only ``<name>`` is given, in which case the call will then | ||
| 335 | - set the same variables as a call to | ||
| 336 | - :command:`FetchContent_Populate(name) <FetchContent_Populate>`. This allows | ||
| 337 | - the following canonical pattern to be used, which ensures that the relevant | ||
| 338 | - variables will always be defined regardless of whether or not the population | ||
| 339 | - has been performed elsewhere in the project already: | ||
| 340 | - | ||
| 341 | - .. code-block:: cmake | ||
| 342 | - | ||
| 343 | - FetchContent_GetProperties(foobar) | ||
| 344 | - if(NOT foobar_POPULATED) | ||
| 345 | - FetchContent_Populate(foobar) | ||
| 346 | - | ||
| 347 | - # Set any custom variables, etc. here, then | ||
| 348 | - # populate the content as part of this build | ||
| 349 | - | ||
| 350 | - add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR}) | ||
| 351 | - endif() | ||
| 352 | - | ||
| 353 | - The above pattern allows other parts of the overall project hierarchy to | ||
| 354 | - re-use the same content and ensure that it is only populated once. | ||
| 355 | - | ||
| 356 | - | ||
| 357 | -.. _`fetch-content-examples`: | ||
| 358 | - | ||
| 359 | -Examples | ||
| 360 | -^^^^^^^^ | ||
| 361 | - | ||
| 362 | -Consider a project hierarchy where ``projA`` is the top level project and it | ||
| 363 | -depends on projects ``projB`` and ``projC``. Both ``projB`` and ``projC`` | ||
| 364 | -can be built standalone and they also both depend on another project | ||
| 365 | -``projD``. For simplicity, this example will assume that all four projects | ||
| 366 | -are available on a company git server. The ``CMakeLists.txt`` of each project | ||
| 367 | -might have sections like the following: | ||
| 368 | - | ||
| 369 | -*projA*: | ||
| 370 | - | ||
| 371 | -.. code-block:: cmake | ||
| 372 | - | ||
| 373 | - include(FetchContent) | ||
| 374 | - FetchContent_Declare( | ||
| 375 | - projB | ||
| 376 | - GIT_REPOSITORY git@mycompany.com/git/projB.git | ||
| 377 | - GIT_TAG 4a89dc7e24ff212a7b5167bef7ab079d | ||
| 378 | - ) | ||
| 379 | - FetchContent_Declare( | ||
| 380 | - projC | ||
| 381 | - GIT_REPOSITORY git@mycompany.com/git/projC.git | ||
| 382 | - GIT_TAG 4ad4016bd1d8d5412d135cf8ceea1bb9 | ||
| 383 | - ) | ||
| 384 | - FetchContent_Declare( | ||
| 385 | - projD | ||
| 386 | - GIT_REPOSITORY git@mycompany.com/git/projD.git | ||
| 387 | - GIT_TAG origin/integrationBranch | ||
| 388 | - ) | ||
| 389 | - | ||
| 390 | - FetchContent_GetProperties(projB) | ||
| 391 | - if(NOT projb_POPULATED) | ||
| 392 | - FetchContent_Populate(projB) | ||
| 393 | - add_subdirectory(${projb_SOURCE_DIR} ${projb_BINARY_DIR}) | ||
| 394 | - endif() | ||
| 395 | - | ||
| 396 | - FetchContent_GetProperties(projC) | ||
| 397 | - if(NOT projc_POPULATED) | ||
| 398 | - FetchContent_Populate(projC) | ||
| 399 | - add_subdirectory(${projc_SOURCE_DIR} ${projc_BINARY_DIR}) | ||
| 400 | - endif() | ||
| 401 | - | ||
| 402 | -*projB*: | ||
| 403 | - | ||
| 404 | -.. code-block:: cmake | ||
| 405 | - | ||
| 406 | - include(FetchContent) | ||
| 407 | - FetchContent_Declare( | ||
| 408 | - projD | ||
| 409 | - GIT_REPOSITORY git@mycompany.com/git/projD.git | ||
| 410 | - GIT_TAG 20b415f9034bbd2a2e8216e9a5c9e632 | ||
| 411 | - ) | ||
| 412 | - | ||
| 413 | - FetchContent_GetProperties(projD) | ||
| 414 | - if(NOT projd_POPULATED) | ||
| 415 | - FetchContent_Populate(projD) | ||
| 416 | - add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR}) | ||
| 417 | - endif() | ||
| 418 | - | ||
| 419 | - | ||
| 420 | -*projC*: | ||
| 421 | - | ||
| 422 | -.. code-block:: cmake | ||
| 423 | - | ||
| 424 | - include(FetchContent) | ||
| 425 | - FetchContent_Declare( | ||
| 426 | - projD | ||
| 427 | - GIT_REPOSITORY git@mycompany.com/git/projD.git | ||
| 428 | - GIT_TAG 7d9a17ad2c962aa13e2fbb8043fb6b8a | ||
| 429 | - ) | ||
| 430 | - | ||
| 431 | - FetchContent_GetProperties(projD) | ||
| 432 | - if(NOT projd_POPULATED) | ||
| 433 | - FetchContent_Populate(projD) | ||
| 434 | - add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR}) | ||
| 435 | - endif() | ||
| 436 | - | ||
| 437 | -A few key points should be noted in the above: | ||
| 438 | - | ||
| 439 | -- ``projB`` and ``projC`` define different content details for ``projD``, | ||
| 440 | - but ``projA`` also defines a set of content details for ``projD`` and | ||
| 441 | - because ``projA`` will define them first, the details from ``projB`` and | ||
| 442 | - ``projC`` will not be used. The override details defined by ``projA`` | ||
| 443 | - are not required to match either of those from ``projB`` or ``projC``, but | ||
| 444 | - it is up to the higher level project to ensure that the details it does | ||
| 445 | - define still make sense for the child projects. | ||
| 446 | -- While ``projA`` defined content details for ``projD``, it did not need | ||
| 447 | - to explicitly call ``FetchContent_Populate(projD)`` itself. Instead, it | ||
| 448 | - leaves that to a child project to do (in this case it will be ``projB`` | ||
| 449 | - since it is added to the build ahead of ``projC``). If ``projA`` needed to | ||
| 450 | - customize how the ``projD`` content was brought into the build as well | ||
| 451 | - (e.g. define some CMake variables before calling | ||
| 452 | - :command:`add_subdirectory` after populating), it would do the call to | ||
| 453 | - ``FetchContent_Populate()``, etc. just as it did for the ``projB`` and | ||
| 454 | - ``projC`` content. For higher level projects, it is usually enough to | ||
| 455 | - just define the override content details and leave the actual population | ||
| 456 | - to the child projects. This saves repeating the same thing at each level | ||
| 457 | - of the project hierarchy unnecessarily. | ||
| 458 | -- Even though ``projA`` is the top level project in this example, it still | ||
| 459 | - checks whether ``projB`` and ``projC`` have already been populated before | ||
| 460 | - going ahead to do those populations. This makes ``projA`` able to be more | ||
| 461 | - easily incorporated as a child of some other higher level project in the | ||
| 462 | - future if required. Always protect a call to | ||
| 463 | - :command:`FetchContent_Populate` with a check to | ||
| 464 | - :command:`FetchContent_GetProperties`, even in what may be considered a top | ||
| 465 | - level project at the time. | ||
| 466 | - | ||
| 467 | - | ||
| 468 | -The following example demonstrates how one might download and unpack a | ||
| 469 | -firmware tarball using CMake's :manual:`script mode <cmake(1)>`. The call to | ||
| 470 | -:command:`FetchContent_Populate` specifies all the content details and the | ||
| 471 | -unpacked firmware will be placed in a ``firmware`` directory below the | ||
| 472 | -current working directory. | ||
| 473 | - | ||
| 474 | -*getFirmware.cmake*: | ||
| 475 | - | ||
| 476 | -.. code-block:: cmake | ||
| 477 | - | ||
| 478 | - # NOTE: Intended to be run in script mode with cmake -P | ||
| 479 | - include(FetchContent) | ||
| 480 | - FetchContent_Populate( | ||
| 481 | - firmware | ||
| 482 | - URL https://mycompany.com/assets/firmware-1.23-arm.tar.gz | ||
| 483 | - URL_HASH MD5=68247684da89b608d466253762b0ff11 | ||
| 484 | - SOURCE_DIR firmware | ||
| 485 | - ) | ||
| 486 | - | ||
| 487 | -#]=======================================================================] | ||
| 488 | - | ||
| 489 | - | ||
| 490 | -set(__FetchContent_privateDir "${CMAKE_CURRENT_LIST_DIR}/FetchContent") | ||
| 491 | - | ||
| 492 | -#======================================================================= | ||
| 493 | -# Recording and retrieving content details for later population | ||
| 494 | -#======================================================================= | ||
| 495 | - | ||
| 496 | -# Internal use, projects must not call this directly. It is | ||
| 497 | -# intended for use by FetchContent_Declare() only. | ||
| 498 | -# | ||
| 499 | -# Sets a content-specific global property (not meant for use | ||
| 500 | -# outside of functions defined here in this file) which can later | ||
| 501 | -# be retrieved using __FetchContent_getSavedDetails() with just the | ||
| 502 | -# same content name. If there is already a value stored in the | ||
| 503 | -# property, it is left unchanged and this call has no effect. | ||
| 504 | -# This allows parent projects to define the content details, | ||
| 505 | -# overriding anything a child project may try to set (properties | ||
| 506 | -# are not cached between runs, so the first thing to set it in a | ||
| 507 | -# build will be in control). | ||
| 508 | -function(__FetchContent_declareDetails contentName) | ||
| 509 | - | ||
| 510 | - string(TOLOWER ${contentName} contentNameLower) | ||
| 511 | - set(propertyName "_FetchContent_${contentNameLower}_savedDetails") | ||
| 512 | - get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED) | ||
| 513 | - if(NOT alreadyDefined) | ||
| 514 | - define_property(GLOBAL PROPERTY ${propertyName} | ||
| 515 | - BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" | ||
| 516 | - FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" | ||
| 517 | - ) | ||
| 518 | - set_property(GLOBAL PROPERTY ${propertyName} ${ARGN}) | ||
| 519 | - endif() | ||
| 520 | - | ||
| 521 | -endfunction() | ||
| 522 | - | ||
| 523 | - | ||
| 524 | -# Internal use, projects must not call this directly. It is | ||
| 525 | -# intended for use by the FetchContent_Declare() function. | ||
| 526 | -# | ||
| 527 | -# Retrieves details saved for the specified content in an | ||
| 528 | -# earlier call to __FetchContent_declareDetails(). | ||
| 529 | -function(__FetchContent_getSavedDetails contentName outVar) | ||
| 530 | - | ||
| 531 | - string(TOLOWER ${contentName} contentNameLower) | ||
| 532 | - set(propertyName "_FetchContent_${contentNameLower}_savedDetails") | ||
| 533 | - get_property(alreadyDefined GLOBAL PROPERTY ${propertyName} DEFINED) | ||
| 534 | - if(NOT alreadyDefined) | ||
| 535 | - message(FATAL_ERROR "No content details recorded for ${contentName}") | ||
| 536 | - endif() | ||
| 537 | - get_property(propertyValue GLOBAL PROPERTY ${propertyName}) | ||
| 538 | - set(${outVar} "${propertyValue}" PARENT_SCOPE) | ||
| 539 | - | ||
| 540 | -endfunction() | ||
| 541 | - | ||
| 542 | - | ||
| 543 | -# Saves population details of the content, sets defaults for the | ||
| 544 | -# SOURCE_DIR and BUILD_DIR. | ||
| 545 | -function(FetchContent_Declare contentName) | ||
| 546 | - | ||
| 547 | - set(options "") | ||
| 548 | - set(oneValueArgs SVN_REPOSITORY) | ||
| 549 | - set(multiValueArgs "") | ||
| 550 | - | ||
| 551 | - cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | ||
| 552 | - | ||
| 553 | - unset(srcDirSuffix) | ||
| 554 | - unset(svnRepoArgs) | ||
| 555 | - if(ARG_SVN_REPOSITORY) | ||
| 556 | - # Add a hash of the svn repository URL to the source dir. This works | ||
| 557 | - # around the problem where if the URL changes, the download would | ||
| 558 | - # fail because it tries to checkout/update rather than switch the | ||
| 559 | - # old URL to the new one. We limit the hash to the first 7 characters | ||
| 560 | - # so that the source path doesn't get overly long (which can be a | ||
| 561 | - # problem on windows due to path length limits). | ||
| 562 | - string(SHA1 urlSHA ${ARG_SVN_REPOSITORY}) | ||
| 563 | - string(SUBSTRING ${urlSHA} 0 7 urlSHA) | ||
| 564 | - set(srcDirSuffix "-${urlSHA}") | ||
| 565 | - set(svnRepoArgs SVN_REPOSITORY ${ARG_SVN_REPOSITORY}) | ||
| 566 | - endif() | ||
| 567 | - | ||
| 568 | - string(TOLOWER ${contentName} contentNameLower) | ||
| 569 | - __FetchContent_declareDetails( | ||
| 570 | - ${contentNameLower} | ||
| 571 | - SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src${srcDirSuffix}" | ||
| 572 | - BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build" | ||
| 573 | - ${svnRepoArgs} | ||
| 574 | - # List these last so they can override things we set above | ||
| 575 | - ${ARG_UNPARSED_ARGUMENTS} | ||
| 576 | - ) | ||
| 577 | - | ||
| 578 | -endfunction() | ||
| 579 | - | ||
| 580 | - | ||
| 581 | -#======================================================================= | ||
| 582 | -# Set/get whether the specified content has been populated yet. | ||
| 583 | -# The setter also records the source and binary dirs used. | ||
| 584 | -#======================================================================= | ||
| 585 | - | ||
| 586 | -# Internal use, projects must not call this directly. It is | ||
| 587 | -# intended for use by the FetchContent_Populate() function to | ||
| 588 | -# record when FetchContent_Populate() is called for a particular | ||
| 589 | -# content name. | ||
| 590 | -function(__FetchContent_setPopulated contentName sourceDir binaryDir) | ||
| 591 | - | ||
| 592 | - string(TOLOWER ${contentName} contentNameLower) | ||
| 593 | - set(prefix "_FetchContent_${contentNameLower}") | ||
| 594 | - | ||
| 595 | - set(propertyName "${prefix}_sourceDir") | ||
| 596 | - define_property(GLOBAL PROPERTY ${propertyName} | ||
| 597 | - BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" | ||
| 598 | - FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" | ||
| 599 | - ) | ||
| 600 | - set_property(GLOBAL PROPERTY ${propertyName} ${sourceDir}) | ||
| 601 | - | ||
| 602 | - set(propertyName "${prefix}_binaryDir") | ||
| 603 | - define_property(GLOBAL PROPERTY ${propertyName} | ||
| 604 | - BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" | ||
| 605 | - FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" | ||
| 606 | - ) | ||
| 607 | - set_property(GLOBAL PROPERTY ${propertyName} ${binaryDir}) | ||
| 608 | - | ||
| 609 | - set(propertyName "${prefix}_populated") | ||
| 610 | - define_property(GLOBAL PROPERTY ${propertyName} | ||
| 611 | - BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" | ||
| 612 | - FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" | ||
| 613 | - ) | ||
| 614 | - set_property(GLOBAL PROPERTY ${propertyName} True) | ||
| 615 | - | ||
| 616 | -endfunction() | ||
| 617 | - | ||
| 618 | - | ||
| 619 | -# Set variables in the calling scope for any of the retrievable | ||
| 620 | -# properties. If no specific properties are requested, variables | ||
| 621 | -# will be set for all retrievable properties. | ||
| 622 | -# | ||
| 623 | -# This function is intended to also be used by projects as the canonical | ||
| 624 | -# way to detect whether they should call FetchContent_Populate() | ||
| 625 | -# and pull the populated source into the build with add_subdirectory(), | ||
| 626 | -# if they are using the populated content in that way. | ||
| 627 | -function(FetchContent_GetProperties contentName) | ||
| 628 | - | ||
| 629 | - string(TOLOWER ${contentName} contentNameLower) | ||
| 630 | - | ||
| 631 | - set(options "") | ||
| 632 | - set(oneValueArgs SOURCE_DIR BINARY_DIR POPULATED) | ||
| 633 | - set(multiValueArgs "") | ||
| 634 | - | ||
| 635 | - cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | ||
| 636 | - | ||
| 637 | - if(NOT ARG_SOURCE_DIR AND | ||
| 638 | - NOT ARG_BINARY_DIR AND | ||
| 639 | - NOT ARG_POPULATED) | ||
| 640 | - # No specific properties requested, provide them all | ||
| 641 | - set(ARG_SOURCE_DIR ${contentNameLower}_SOURCE_DIR) | ||
| 642 | - set(ARG_BINARY_DIR ${contentNameLower}_BINARY_DIR) | ||
| 643 | - set(ARG_POPULATED ${contentNameLower}_POPULATED) | ||
| 644 | - endif() | ||
| 645 | - | ||
| 646 | - set(prefix "_FetchContent_${contentNameLower}") | ||
| 647 | - | ||
| 648 | - if(ARG_SOURCE_DIR) | ||
| 649 | - set(propertyName "${prefix}_sourceDir") | ||
| 650 | - get_property(value GLOBAL PROPERTY ${propertyName}) | ||
| 651 | - if(value) | ||
| 652 | - set(${ARG_SOURCE_DIR} ${value} PARENT_SCOPE) | ||
| 653 | - endif() | ||
| 654 | - endif() | ||
| 655 | - | ||
| 656 | - if(ARG_BINARY_DIR) | ||
| 657 | - set(propertyName "${prefix}_binaryDir") | ||
| 658 | - get_property(value GLOBAL PROPERTY ${propertyName}) | ||
| 659 | - if(value) | ||
| 660 | - set(${ARG_BINARY_DIR} ${value} PARENT_SCOPE) | ||
| 661 | - endif() | ||
| 662 | - endif() | ||
| 663 | - | ||
| 664 | - if(ARG_POPULATED) | ||
| 665 | - set(propertyName "${prefix}_populated") | ||
| 666 | - get_property(value GLOBAL PROPERTY ${propertyName} DEFINED) | ||
| 667 | - set(${ARG_POPULATED} ${value} PARENT_SCOPE) | ||
| 668 | - endif() | ||
| 669 | - | ||
| 670 | -endfunction() | ||
| 671 | - | ||
| 672 | - | ||
| 673 | -#======================================================================= | ||
| 674 | -# Performing the population | ||
| 675 | -#======================================================================= | ||
| 676 | - | ||
| 677 | -# The value of contentName will always have been lowercased by the caller. | ||
| 678 | -# All other arguments are assumed to be options that are understood by | ||
| 679 | -# ExternalProject_Add(), except for QUIET and SUBBUILD_DIR. | ||
| 680 | -function(__FetchContent_directPopulate contentName) | ||
| 681 | - | ||
| 682 | - set(options | ||
| 683 | - QUIET | ||
| 684 | - ) | ||
| 685 | - set(oneValueArgs | ||
| 686 | - SUBBUILD_DIR | ||
| 687 | - SOURCE_DIR | ||
| 688 | - BINARY_DIR | ||
| 689 | - # Prevent the following from being passed through | ||
| 690 | - CONFIGURE_COMMAND | ||
| 691 | - BUILD_COMMAND | ||
| 692 | - INSTALL_COMMAND | ||
| 693 | - TEST_COMMAND | ||
| 694 | - ) | ||
| 695 | - set(multiValueArgs "") | ||
| 696 | - | ||
| 697 | - cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) | ||
| 698 | - | ||
| 699 | - if(NOT ARG_SUBBUILD_DIR) | ||
| 700 | - message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set") | ||
| 701 | - elseif(NOT IS_ABSOLUTE "${ARG_SUBBUILD_DIR}") | ||
| 702 | - set(ARG_SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBBUILD_DIR}") | ||
| 703 | - endif() | ||
| 704 | - | ||
| 705 | - if(NOT ARG_SOURCE_DIR) | ||
| 706 | - message(FATAL_ERROR "Internal error: SOURCE_DIR not set") | ||
| 707 | - elseif(NOT IS_ABSOLUTE "${ARG_SOURCE_DIR}") | ||
| 708 | - set(ARG_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SOURCE_DIR}") | ||
| 709 | - endif() | ||
| 710 | - | ||
| 711 | - if(NOT ARG_BINARY_DIR) | ||
| 712 | - message(FATAL_ERROR "Internal error: BINARY_DIR not set") | ||
| 713 | - elseif(NOT IS_ABSOLUTE "${ARG_BINARY_DIR}") | ||
| 714 | - set(ARG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_BINARY_DIR}") | ||
| 715 | - endif() | ||
| 716 | - | ||
| 717 | - # Ensure the caller can know where to find the source and build directories | ||
| 718 | - # with some convenient variables. Doing this here ensures the caller sees | ||
| 719 | - # the correct result in the case where the default values are overridden by | ||
| 720 | - # the content details set by the project. | ||
| 721 | - set(${contentName}_SOURCE_DIR "${ARG_SOURCE_DIR}" PARENT_SCOPE) | ||
| 722 | - set(${contentName}_BINARY_DIR "${ARG_BINARY_DIR}" PARENT_SCOPE) | ||
| 723 | - | ||
| 724 | - # The unparsed arguments may contain spaces, so build up ARG_EXTRA | ||
| 725 | - # in such a way that it correctly substitutes into the generated | ||
| 726 | - # CMakeLists.txt file with each argument quoted. | ||
| 727 | - unset(ARG_EXTRA) | ||
| 728 | - foreach(arg IN LISTS ARG_UNPARSED_ARGUMENTS) | ||
| 729 | - set(ARG_EXTRA "${ARG_EXTRA} \"${arg}\"") | ||
| 730 | - endforeach() | ||
| 731 | - | ||
| 732 | - # Hide output if requested, but save it to a variable in case there's an | ||
| 733 | - # error so we can show the output upon failure. When not quiet, don't | ||
| 734 | - # capture the output to a variable because the user may want to see the | ||
| 735 | - # output as it happens (e.g. progress during long downloads). Combine both | ||
| 736 | - # stdout and stderr in the one capture variable so the output stays in order. | ||
| 737 | - if (ARG_QUIET) | ||
| 738 | - set(outputOptions | ||
| 739 | - OUTPUT_VARIABLE capturedOutput | ||
| 740 | - ERROR_VARIABLE capturedOutput | ||
| 741 | - ) | ||
| 742 | - else() | ||
| 743 | - set(capturedOutput) | ||
| 744 | - set(outputOptions) | ||
| 745 | - message(STATUS "Populating ${contentName}") | ||
| 746 | - endif() | ||
| 747 | - | ||
| 748 | - if(CMAKE_GENERATOR) | ||
| 749 | - set(generatorOpts "-G${CMAKE_GENERATOR}") | ||
| 750 | - if(CMAKE_GENERATOR_PLATFORM) | ||
| 751 | - list(APPEND generatorOpts "-A${CMAKE_GENERATOR_PLATFORM}") | ||
| 752 | - endif() | ||
| 753 | - if(CMAKE_GENERATOR_TOOLSET) | ||
| 754 | - list(APPEND generatorOpts "-T${CMAKE_GENERATOR_TOOLSET}") | ||
| 755 | - endif() | ||
| 756 | - | ||
| 757 | - if(CMAKE_MAKE_PROGRAM) | ||
| 758 | - list(APPEND generatorOpts "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}") | ||
| 759 | - endif() | ||
| 760 | - | ||
| 761 | - else() | ||
| 762 | - # Likely we've been invoked via CMake's script mode where no | ||
| 763 | - # generator is set (and hence CMAKE_MAKE_PROGRAM could not be | ||
| 764 | - # trusted even if provided). We will have to rely on being | ||
| 765 | - # able to find the default generator and build tool. | ||
| 766 | - unset(generatorOpts) | ||
| 767 | - endif() | ||
| 768 | - | ||
| 769 | - # Create and build a separate CMake project to carry out the population. | ||
| 770 | - # If we've already previously done these steps, they will not cause | ||
| 771 | - # anything to be updated, so extra rebuilds of the project won't occur. | ||
| 772 | - # Make sure to pass through CMAKE_MAKE_PROGRAM in case the main project | ||
| 773 | - # has this set to something not findable on the PATH. | ||
| 774 | - configure_file("${__FetchContent_privateDir}/CMakeLists.cmake.in" | ||
| 775 | - "${ARG_SUBBUILD_DIR}/CMakeLists.txt") | ||
| 776 | - execute_process( | ||
| 777 | - COMMAND ${CMAKE_COMMAND} ${generatorOpts} . | ||
| 778 | - RESULT_VARIABLE result | ||
| 779 | - ${outputOptions} | ||
| 780 | - WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" | ||
| 781 | - ) | ||
| 782 | - if(result) | ||
| 783 | - if(capturedOutput) | ||
| 784 | - message("${capturedOutput}") | ||
| 785 | - endif() | ||
| 786 | - message(FATAL_ERROR "CMake step for ${contentName} failed: ${result}") | ||
| 787 | - endif() | ||
| 788 | - execute_process( | ||
| 789 | - COMMAND ${CMAKE_COMMAND} --build . | ||
| 790 | - RESULT_VARIABLE result | ||
| 791 | - ${outputOptions} | ||
| 792 | - WORKING_DIRECTORY "${ARG_SUBBUILD_DIR}" | ||
| 793 | - ) | ||
| 794 | - if(result) | ||
| 795 | - if(capturedOutput) | ||
| 796 | - message("${capturedOutput}") | ||
| 797 | - endif() | ||
| 798 | - message(FATAL_ERROR "Build step for ${contentName} failed: ${result}") | ||
| 799 | - endif() | ||
| 800 | - | ||
| 801 | -endfunction() | ||
| 802 | - | ||
| 803 | - | ||
| 804 | -option(FETCHCONTENT_FULLY_DISCONNECTED "Disables all attempts to download or update content and assumes source dirs already exist") | ||
| 805 | -option(FETCHCONTENT_UPDATES_DISCONNECTED "Enables UPDATE_DISCONNECTED behavior for all content population") | ||
| 806 | -option(FETCHCONTENT_QUIET "Enables QUIET option for all content population" ON) | ||
| 807 | -set(FETCHCONTENT_BASE_DIR "${CMAKE_BINARY_DIR}/_deps" CACHE PATH "Directory under which to collect all populated content") | ||
| 808 | - | ||
| 809 | -# Populate the specified content using details stored from | ||
| 810 | -# an earlier call to FetchContent_Declare(). | ||
| 811 | -function(FetchContent_Populate contentName) | ||
| 812 | - | ||
| 813 | - if(NOT contentName) | ||
| 814 | - message(FATAL_ERROR "Empty contentName not allowed for FetchContent_Populate()") | ||
| 815 | - endif() | ||
| 816 | - | ||
| 817 | - string(TOLOWER ${contentName} contentNameLower) | ||
| 818 | - | ||
| 819 | - if(ARGN) | ||
| 820 | - # This is the direct population form with details fully specified | ||
| 821 | - # as part of the call, so we already have everything we need | ||
| 822 | - __FetchContent_directPopulate( | ||
| 823 | - ${contentNameLower} | ||
| 824 | - SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-subbuild" | ||
| 825 | - SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-src" | ||
| 826 | - BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-build" | ||
| 827 | - ${ARGN} # Could override any of the above ..._DIR variables | ||
| 828 | - ) | ||
| 829 | - | ||
| 830 | - # Pass source and binary dir variables back to the caller | ||
| 831 | - set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE) | ||
| 832 | - set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE) | ||
| 833 | - | ||
| 834 | - # Don't set global properties, or record that we did this population, since | ||
| 835 | - # this was a direct call outside of the normal declared details form. | ||
| 836 | - # We only want to save values in the global properties for content that | ||
| 837 | - # honours the hierarchical details mechanism so that projects are not | ||
| 838 | - # robbed of the ability to override details set in nested projects. | ||
| 839 | - return() | ||
| 840 | - endif() | ||
| 841 | - | ||
| 842 | - # No details provided, so assume they were saved from an earlier call | ||
| 843 | - # to FetchContent_Declare(). Do a check that we haven't already | ||
| 844 | - # populated this content before in case the caller forgot to check. | ||
| 845 | - FetchContent_GetProperties(${contentName}) | ||
| 846 | - if(${contentNameLower}_POPULATED) | ||
| 847 | - message(FATAL_ERROR "Content ${contentName} already populated in ${${contentNameLower}_SOURCE_DIR}") | ||
| 848 | - endif() | ||
| 849 | - | ||
| 850 | - string(TOUPPER ${contentName} contentNameUpper) | ||
| 851 | - set(FETCHCONTENT_SOURCE_DIR_${contentNameUpper} | ||
| 852 | - "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}" | ||
| 853 | - CACHE PATH "When not empty, overrides where to find pre-populated content for ${contentName}") | ||
| 854 | - | ||
| 855 | - if(FETCHCONTENT_SOURCE_DIR_${contentNameUpper}) | ||
| 856 | - # The source directory has been explicitly provided in the cache, | ||
| 857 | - # so no population is required | ||
| 858 | - set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_SOURCE_DIR_${contentNameUpper}}") | ||
| 859 | - set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") | ||
| 860 | - | ||
| 861 | - elseif(FETCHCONTENT_FULLY_DISCONNECTED) | ||
| 862 | - # Bypass population and assume source is already there from a previous run | ||
| 863 | - set(${contentNameLower}_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src") | ||
| 864 | - set(${contentNameLower}_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build") | ||
| 865 | - | ||
| 866 | - else() | ||
| 867 | - # Support both a global "disconnect all updates" and a per-content | ||
| 868 | - # update test (either one being set disables updates for this content). | ||
| 869 | - option(FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper} | ||
| 870 | - "Enables UPDATE_DISCONNECTED behavior just for population of ${contentName}") | ||
| 871 | - if(FETCHCONTENT_UPDATES_DISCONNECTED OR | ||
| 872 | - FETCHCONTENT_UPDATES_DISCONNECTED_${contentNameUpper}) | ||
| 873 | - set(disconnectUpdates True) | ||
| 874 | - else() | ||
| 875 | - set(disconnectUpdates False) | ||
| 876 | - endif() | ||
| 877 | - | ||
| 878 | - if(FETCHCONTENT_QUIET) | ||
| 879 | - set(quietFlag QUIET) | ||
| 880 | - else() | ||
| 881 | - unset(quietFlag) | ||
| 882 | - endif() | ||
| 883 | - | ||
| 884 | - __FetchContent_getSavedDetails(${contentName} contentDetails) | ||
| 885 | - if("${contentDetails}" STREQUAL "") | ||
| 886 | - message(FATAL_ERROR "No details have been set for content: ${contentName}") | ||
| 887 | - endif() | ||
| 888 | - | ||
| 889 | - __FetchContent_directPopulate( | ||
| 890 | - ${contentNameLower} | ||
| 891 | - ${quietFlag} | ||
| 892 | - UPDATE_DISCONNECTED ${disconnectUpdates} | ||
| 893 | - SUBBUILD_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-subbuild" | ||
| 894 | - SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src" | ||
| 895 | - BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build" | ||
| 896 | - # Put the saved details last so they can override any of the | ||
| 897 | - # the options we set above (this can include SOURCE_DIR or | ||
| 898 | - # BUILD_DIR) | ||
| 899 | - ${contentDetails} | ||
| 900 | - ) | ||
| 901 | - endif() | ||
| 902 | - | ||
| 903 | - __FetchContent_setPopulated( | ||
| 904 | - ${contentName} | ||
| 905 | - ${${contentNameLower}_SOURCE_DIR} | ||
| 906 | - ${${contentNameLower}_BINARY_DIR} | ||
| 907 | - ) | ||
| 908 | - | ||
| 909 | - # Pass variables back to the caller. The variables passed back here | ||
| 910 | - # must match what FetchContent_GetProperties() sets when it is called | ||
| 911 | - # with just the content name. | ||
| 912 | - set(${contentNameLower}_SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}" PARENT_SCOPE) | ||
| 913 | - set(${contentNameLower}_BINARY_DIR "${${contentNameLower}_BINARY_DIR}" PARENT_SCOPE) | ||
| 914 | - set(${contentNameLower}_POPULATED True PARENT_SCOPE) | ||
| 915 | - | ||
| 916 | -endfunction() |
| 1 | -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying | ||
| 2 | -# file Copyright.txt or https://cmake.org/licensing for details. | ||
| 3 | - | ||
| 4 | -cmake_minimum_required(VERSION ${CMAKE_VERSION}) | ||
| 5 | - | ||
| 6 | -# We name the project and the target for the ExternalProject_Add() call | ||
| 7 | -# to something that will highlight to the user what we are working on if | ||
| 8 | -# something goes wrong and an error message is produced. | ||
| 9 | - | ||
| 10 | -project(${contentName}-populate NONE) | ||
| 11 | - | ||
| 12 | -include(ExternalProject) | ||
| 13 | -ExternalProject_Add(${contentName}-populate | ||
| 14 | - ${ARG_EXTRA} | ||
| 15 | - SOURCE_DIR "${ARG_SOURCE_DIR}" | ||
| 16 | - BINARY_DIR "${ARG_BINARY_DIR}" | ||
| 17 | - CONFIGURE_COMMAND "" | ||
| 18 | - BUILD_COMMAND "" | ||
| 19 | - INSTALL_COMMAND "" | ||
| 20 | - TEST_COMMAND "" | ||
| 21 | -) |
cmake/Modules/README.md
已删除
100644 → 0
| @@ -14,18 +14,26 @@ | @@ -14,18 +14,26 @@ | ||
| 14 | # limitations under the License. | 14 | # limitations under the License. |
| 15 | 15 | ||
| 16 | function(download_googltest) | 16 | function(download_googltest) |
| 17 | - if(CMAKE_VERSION VERSION_LESS 3.11) | ||
| 18 | - # FetchContent is available since 3.11, | ||
| 19 | - # we've copied it to ${CMAKE_SOURCE_DIR}/cmake/Modules | ||
| 20 | - # so that it can be used in lower CMake versions. | ||
| 21 | - message(STATUS "Use FetchContent provided by sherpa-onnx") | ||
| 22 | - list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) | ||
| 23 | - endif() | ||
| 24 | - | ||
| 25 | include(FetchContent) | 17 | include(FetchContent) |
| 26 | 18 | ||
| 27 | - set(googletest_URL "https://github.com/google/googletest/archive/release-1.10.0.tar.gz") | ||
| 28 | - set(googletest_HASH "SHA256=9dc9157a9a1551ec7a7e43daea9a694a0bb5fb8bec81235d8a1e6ef64c716dcb") | 19 | + set(googletest_URL "https://github.com/google/googletest/archive/refs/tags/v1.13.0.tar.gz") |
| 20 | + set(googletest_HASH "SHA256=ad7fdba11ea011c1d925b3289cf4af2c66a352e18d4c7264392fead75e919363") | ||
| 21 | + | ||
| 22 | + # If you don't have access to the Internet, | ||
| 23 | + # please pre-download googletest | ||
| 24 | + set(possible_file_locations | ||
| 25 | + $ENV{HOME}/Downloads/googletest-1.13.0.tar.gz | ||
| 26 | + ${PROJECT_SOURCE_DIR}/googletest-1.13.0.tar.gz | ||
| 27 | + ${PROJECT_BINARY_DIR}/googletest-1.13.0.tar.gz | ||
| 28 | + /tmp/googletest-1.13.0.tar.gz | ||
| 29 | + ) | ||
| 30 | + | ||
| 31 | + foreach(f IN LISTS possible_file_locations) | ||
| 32 | + if(EXISTS ${f}) | ||
| 33 | + set(googletest_URL "file://${f}") | ||
| 34 | + break() | ||
| 35 | + endif() | ||
| 36 | + endforeach() | ||
| 29 | 37 | ||
| 30 | set(BUILD_GMOCK ON CACHE BOOL "" FORCE) | 38 | set(BUILD_GMOCK ON CACHE BOOL "" FORCE) |
| 31 | set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) | 39 | set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) |
| @@ -39,7 +47,7 @@ function(download_googltest) | @@ -39,7 +47,7 @@ function(download_googltest) | ||
| 39 | 47 | ||
| 40 | FetchContent_GetProperties(googletest) | 48 | FetchContent_GetProperties(googletest) |
| 41 | if(NOT googletest_POPULATED) | 49 | if(NOT googletest_POPULATED) |
| 42 | - message(STATUS "Downloading googletest") | 50 | + message(STATUS "Downloading googletest from ${googletest_URL}") |
| 43 | FetchContent_Populate(googletest) | 51 | FetchContent_Populate(googletest) |
| 44 | endif() | 52 | endif() |
| 45 | message(STATUS "googletest is downloaded to ${googletest_SOURCE_DIR}") | 53 | message(STATUS "googletest is downloaded to ${googletest_SOURCE_DIR}") |
| 1 | function(download_kaldi_native_fbank) | 1 | function(download_kaldi_native_fbank) |
| 2 | - if(CMAKE_VERSION VERSION_LESS 3.11) | ||
| 3 | - # FetchContent is available since 3.11, | ||
| 4 | - # we've copied it to ${CMAKE_SOURCE_DIR}/cmake/Modules | ||
| 5 | - # so that it can be used in lower CMake versions. | ||
| 6 | - message(STATUS "Use FetchContent provided by sherpa-onnx") | ||
| 7 | - list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) | ||
| 8 | - endif() | ||
| 9 | - | ||
| 10 | include(FetchContent) | 2 | include(FetchContent) |
| 11 | 3 | ||
| 12 | - set(kaldi_native_fbank_URL "https://github.com/csukuangfj/kaldi-native-fbank/archive/refs/tags/v1.5.tar.gz") | ||
| 13 | - set(kaldi_native_fbank_HASH "SHA256=632c68adf8f6de831198a2a0e4c1920b31d5a1de263dcac5105be9da99f40bd5") | 4 | + set(kaldi_native_fbank_URL "https://github.com/csukuangfj/kaldi-native-fbank/archive/refs/tags/v1.11.tar.gz") |
| 5 | + set(kaldi_native_fbank_HASH "SHA256=e69ae25ef6f30566ef31ca949dd1b0b8ec3a827caeba93a61d82bb848dac5d69") | ||
| 14 | 6 | ||
| 15 | set(KALDI_NATIVE_FBANK_BUILD_TESTS OFF CACHE BOOL "" FORCE) | 7 | set(KALDI_NATIVE_FBANK_BUILD_TESTS OFF CACHE BOOL "" FORCE) |
| 16 | set(KALDI_NATIVE_FBANK_BUILD_PYTHON OFF CACHE BOOL "" FORCE) | 8 | set(KALDI_NATIVE_FBANK_BUILD_PYTHON OFF CACHE BOOL "" FORCE) |
| 9 | + set(KALDI_NATIVE_FBANK_ENABLE_CHECK OFF CACHE BOOL "" FORCE) | ||
| 10 | + | ||
| 11 | + # If you don't have access to the Internet, | ||
| 12 | + # please pre-download kaldi-native-fbank | ||
| 13 | + set(possible_file_locations | ||
| 14 | + $ENV{HOME}/Downloads/kaldi-native-fbank-1.11.tar.gz | ||
| 15 | + ${PROJECT_SOURCE_DIR}/kaldi-native-fbank-1.11.tar.gz | ||
| 16 | + ${PROJECT_BINARY_DIR}/kaldi-native-fbank-1.11.tar.gz | ||
| 17 | + /tmp/kaldi-native-fbank-1.11.tar.gz | ||
| 18 | + ) | ||
| 19 | + | ||
| 20 | + foreach(f IN LISTS possible_file_locations) | ||
| 21 | + if(EXISTS ${f}) | ||
| 22 | + set(kaldi_native_fbank_URL "file://${f}") | ||
| 23 | + break() | ||
| 24 | + endif() | ||
| 25 | + endforeach() | ||
| 17 | 26 | ||
| 18 | FetchContent_Declare(kaldi_native_fbank | 27 | FetchContent_Declare(kaldi_native_fbank |
| 19 | URL ${kaldi_native_fbank_URL} | 28 | URL ${kaldi_native_fbank_URL} |
| @@ -22,7 +31,7 @@ function(download_kaldi_native_fbank) | @@ -22,7 +31,7 @@ function(download_kaldi_native_fbank) | ||
| 22 | 31 | ||
| 23 | FetchContent_GetProperties(kaldi_native_fbank) | 32 | FetchContent_GetProperties(kaldi_native_fbank) |
| 24 | if(NOT kaldi_native_fbank_POPULATED) | 33 | if(NOT kaldi_native_fbank_POPULATED) |
| 25 | - message(STATUS "Downloading kaldi-native-fbank ${kaldi_native_fbank_URL}") | 34 | + message(STATUS "Downloading kaldi-native-fbank from ${kaldi_native_fbank_URL}") |
| 26 | FetchContent_Populate(kaldi_native_fbank) | 35 | FetchContent_Populate(kaldi_native_fbank) |
| 27 | endif() | 36 | endif() |
| 28 | message(STATUS "kaldi-native-fbank is downloaded to ${kaldi_native_fbank_SOURCE_DIR}") | 37 | message(STATUS "kaldi-native-fbank is downloaded to ${kaldi_native_fbank_SOURCE_DIR}") |
| 1 | function(download_onnxruntime) | 1 | function(download_onnxruntime) |
| 2 | - if(CMAKE_VERSION VERSION_LESS 3.11) | ||
| 3 | - # FetchContent is available since 3.11, | ||
| 4 | - # we've copied it to ${CMAKE_SOURCE_DIR}/cmake/Modules | ||
| 5 | - # so that it can be used in lower CMake versions. | ||
| 6 | - message(STATUS "Use FetchContent provided by sherpa-onnx") | ||
| 7 | - list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) | ||
| 8 | - endif() | ||
| 9 | - | ||
| 10 | include(FetchContent) | 2 | include(FetchContent) |
| 11 | 3 | ||
| 12 | if(UNIX AND NOT APPLE) | 4 | if(UNIX AND NOT APPLE) |
| 13 | - set(onnxruntime_URL "https://github.com/microsoft/onnxruntime/releases/download/v1.12.1/onnxruntime-linux-x64-1.12.1.tgz") | ||
| 14 | - | ||
| 15 | - # If you don't have access to the internet, you can first download onnxruntime to some directory, and the use | ||
| 16 | - # set(onnxruntime_URL "file:///ceph-fj/fangjun/open-source/sherpa-onnx/onnxruntime-linux-x64-1.12.1.tgz") | 5 | + # If you don't have access to the Internet, |
| 6 | + # please pre-download onnxruntime | ||
| 7 | + set(possible_file_locations | ||
| 8 | + $ENV{HOME}/Downloads/onnxruntime-linux-x64-1.14.0.tgz | ||
| 9 | + ${PROJECT_SOURCE_DIR}/onnxruntime-linux-x64-1.14.0.tgz | ||
| 10 | + ${PROJECT_BINARY_DIR}/onnxruntime-linux-x64-1.14.0.tgz | ||
| 11 | + /tmp/onnxruntime-linux-x64-1.14.0.tgz | ||
| 12 | + ) | ||
| 17 | 13 | ||
| 18 | - set(onnxruntime_HASH "SHA256=8f6eb9e2da9cf74e7905bf3fc687ef52e34cc566af7af2f92dafe5a5d106aa3d") | 14 | + set(onnxruntime_URL "https://github.com/microsoft/onnxruntime/releases/download/v1.14.0/onnxruntime-linux-x64-1.14.0.tgz") |
| 15 | + set(onnxruntime_HASH "SHA256=92bf534e5fa5820c8dffe9de2850f84ed2a1c063e47c659ce09e8c7938aa2090") | ||
| 19 | # After downloading, it contains: | 16 | # After downloading, it contains: |
| 20 | - # ./lib/libonnxruntime.so.1.12.1 | ||
| 21 | - # ./lib/libonnxruntime.so, which is a symlink to lib/libonnxruntime.so.1.12.1 | 17 | + # ./lib/libonnxruntime.so.1.14.0 |
| 18 | + # ./lib/libonnxruntime.so, which is a symlink to lib/libonnxruntime.so.1.14.0 | ||
| 22 | # | 19 | # |
| 23 | # ./include | 20 | # ./include |
| 24 | # It contains all the needed header files | 21 | # It contains all the needed header files |
| 25 | elseif(APPLE) | 22 | elseif(APPLE) |
| 26 | - set(onnxruntime_URL "https://github.com/microsoft/onnxruntime/releases/download/v1.12.1/onnxruntime-osx-x86_64-1.12.1.tgz") | ||
| 27 | - set(onnxruntime_HASH "SHA256=10921c2e75817edcbfc8b29882612be07e25dd33c8449d5892a9d45588898099") | 23 | + # If you don't have access to the Internet, |
| 24 | + # please pre-download onnxruntime | ||
| 25 | + set(possible_file_locations | ||
| 26 | + $ENV{HOME}/Downloads/onnxruntime-osx-x86_64-1.14.0.tgz | ||
| 27 | + ${PROJECT_SOURCE_DIR}/onnxruntime-osx-x86_64-1.14.0.tgz | ||
| 28 | + ${PROJECT_BINARY_DIR}/onnxruntime-osx-x86_64-1.14.0.tgz | ||
| 29 | + /tmp/onnxruntime-osx-x86_64-1.14.0.tgz | ||
| 30 | + ) | ||
| 31 | + set(onnxruntime_URL "https://github.com/microsoft/onnxruntime/releases/download/v1.14.0/onnxruntime-osx-x86_64-1.14.0.tgz") | ||
| 32 | + set(onnxruntime_HASH "SHA256=abd2056d56051e78263af37b8dffc3e6da110d2937af7a581a34d1a58dc58360") | ||
| 28 | # After downloading, it contains: | 33 | # After downloading, it contains: |
| 29 | - # ./lib/libonnxruntime.1.12.1.dylib | ||
| 30 | - # ./lib/libonnxruntime.dylib, which is a symlink to lib/libonnxruntime.1.12.1.dylib | 34 | + # ./lib/libonnxruntime.1.14.0.dylib |
| 35 | + # ./lib/libonnxruntime.dylib, which is a symlink to lib/libonnxruntime.1.14.0.dylib | ||
| 31 | # | 36 | # |
| 32 | # ./include | 37 | # ./include |
| 33 | # It contains all the needed header files | 38 | # It contains all the needed header files |
| 34 | elseif(WIN32) | 39 | elseif(WIN32) |
| 35 | - set(onnxruntime_URL "https://github.com/microsoft/onnxruntime/releases/download/v1.12.1/onnxruntime-win-x64-1.12.1.zip") | ||
| 36 | - set(onnxruntime_HASH "SHA256=c69650ba14aeae5903b05256a82e77164fff2de992072bc695a3838c1830b85a") | 40 | + # If you don't have access to the Internet, |
| 41 | + # please pre-download onnxruntime | ||
| 42 | + set(possible_file_locations | ||
| 43 | + $ENV{HOME}/Downloads/onnxruntime-win-x64-1.14.0.zip | ||
| 44 | + ${PROJECT_SOURCE_DIR}/onnxruntime-win-x64-1.14.0.zip | ||
| 45 | + ${PROJECT_BINARY_DIR}/onnxruntime-win-x64-1.14.0.zip | ||
| 46 | + /tmp/onnxruntime-win-x64-1.14.0.zip | ||
| 47 | + ) | ||
| 48 | + set(onnxruntime_URL "https://github.com/microsoft/onnxruntime/releases/download/v1.14.0/onnxruntime-win-x64-1.14.0.zip") | ||
| 49 | + set(onnxruntime_HASH "SHA256=300eafef456748cde2743ee08845bd40ff1bab723697ff934eba6d4ce3519620") | ||
| 37 | # After downloading, it contains: | 50 | # After downloading, it contains: |
| 38 | # ./lib/onnxruntime.{dll,lib,pdb} | 51 | # ./lib/onnxruntime.{dll,lib,pdb} |
| 39 | # ./lib/onnxruntime_providers_shared.{dll,lib,pdb} | 52 | # ./lib/onnxruntime_providers_shared.{dll,lib,pdb} |
| @@ -44,6 +57,13 @@ function(download_onnxruntime) | @@ -44,6 +57,13 @@ function(download_onnxruntime) | ||
| 44 | message(FATAL_ERROR "Only support Linux and macOS at present. Will support other OSes later") | 57 | message(FATAL_ERROR "Only support Linux and macOS at present. Will support other OSes later") |
| 45 | endif() | 58 | endif() |
| 46 | 59 | ||
| 60 | + foreach(f IN LISTS possible_file_locations) | ||
| 61 | + if(EXISTS ${f}) | ||
| 62 | + set(onnxruntime_URL "file://${f}") | ||
| 63 | + break() | ||
| 64 | + endif() | ||
| 65 | + endforeach() | ||
| 66 | + | ||
| 47 | FetchContent_Declare(onnxruntime | 67 | FetchContent_Declare(onnxruntime |
| 48 | URL ${onnxruntime_URL} | 68 | URL ${onnxruntime_URL} |
| 49 | URL_HASH ${onnxruntime_HASH} | 69 | URL_HASH ${onnxruntime_HASH} |
| @@ -51,7 +71,7 @@ function(download_onnxruntime) | @@ -51,7 +71,7 @@ function(download_onnxruntime) | ||
| 51 | 71 | ||
| 52 | FetchContent_GetProperties(onnxruntime) | 72 | FetchContent_GetProperties(onnxruntime) |
| 53 | if(NOT onnxruntime_POPULATED) | 73 | if(NOT onnxruntime_POPULATED) |
| 54 | - message(STATUS "Downloading onnxruntime ${onnxruntime_URL}") | 74 | + message(STATUS "Downloading onnxruntime from ${onnxruntime_URL}") |
| 55 | FetchContent_Populate(onnxruntime) | 75 | FetchContent_Populate(onnxruntime) |
| 56 | endif() | 76 | endif() |
| 57 | message(STATUS "onnxruntime is downloaded to ${onnxruntime_SOURCE_DIR}") | 77 | message(STATUS "onnxruntime is downloaded to ${onnxruntime_SOURCE_DIR}") |
-
请 注册 或 登录 后发表评论