¿Cómo descargar automáticamente las dependencias de C++ de forma multiplataforma + CMake?

7 minutos de lectura

Avatar de usuario de JBeurer
JBeurer

Básicamente quiero lograr este flujo de trabajo:

  1. Pago desde el repositorio en el sistema de Windows (o cualquier plataforma para el caso).

  2. Ejecute alguna herramienta que obtenga dependencias, tanto include como libs, y las coloque en el lugar adecuado (como en “\Program Files\Microsoft Visual Studio 10.0\VC\Lib and \Includes” en Windows)

  3. Ejecute CMake (crea proyectos MSVS en Win)

  4. Abra el proyecto MSVS y compílelo.

Y me gustaría tener este flujo de trabajo en la mayoría de las plataformas.

No quiero tener que descargar dependencias manualmente

¿Cómo hacer esto sin almacenar dependencias en el repositorio? ¿Cuál es la mejor manera de lograr esto?

  • ¿Ya has encontrado alguna solución a tu problema? He estado buscando algo similar a npm para crear programas C/C++ con dependencias.

    – hg.

    9 de noviembre de 2015 a las 6:58

  • @hg. hasta donde yo sé, no hay un administrador de paquetes multiplataforma para C++.

    – JBeurer

    15 de noviembre de 2015 a las 0:24

En CMake puedes usar file(DOWNLOAD URL PATH) a descargar un archivocombine esto con comandos personalizados para descargar y descomprimir:

set(MY_URL "http://...")
set(MY_DOWNLOAD_PATH "path/to/download/to")
set(MY_EXTRACTED_FILE "path/to/extracted/file")

if (NOT EXISTS "${MY_DOWNLOAD_PATH}")
    file(DOWNLOAD "${MY_URL}" "${MY_DOWNLOAD_PATH}")
endif()

add_custom_command(
    OUTPUT "${MY_EXTRACTED_FILE}"
    COMMAND command to unpack
    DEPENDS "${MY_DOWNLOAD_PATH}")

Su objetivo debe depender de la salida del comando personalizado, luego, cuando ejecute CMake, el archivo se descargará y cuando lo construya, extraiga y use.

Todo esto podría incluirse en una macro para que sea más fácil de usar.

También podría considerar usar el módulo CMake ExternoProyecto que puede hacer lo que quieras.

  • Pero esta no es una forma multiplataforma de configurar e instalar las dependencias, ni las dependencias se descargan de un repositorio común.

    – JBeurer

    16 de noviembre de 2011 a las 15:07

  • Usando CMake file(DOWNLOAD ...) o ExternalProject es multiplataforma. Si las dependencias son paquetes, entonces en Windows necesitaría usar msiexec para la instalación del paquete MSI; en Linux, depende de la distribución: en derivados de Red Hat yum o rpm con paquetes RPM, en derivados de Debian apt o dpkg con paquetes DEB.

    – Silas Parker

    16 de noviembre de 2011 a las 15:15

  • Sí, el comando de descarga es multiplataforma, sin embargo, esta no es una forma multiplataforma de INSTALAR dependencias. Cada plataforma suele tener diferentes archivos y paquetes (y, por lo tanto, enlaces de descarga) para la misma dependencia. Se podría decir que estoy buscando algo como Maven para las dependencias de C++ que encajaría bien con CMake.

    – JBeurer

    16 de noviembre de 2011 a las 15:38


  • @JBeurer Si realmente tienes que hacerlo instalar estas dependencias de antemano, esto podría no ser una buena idea en absoluto; Más bien, simplemente agregue cualquier dependencia que tenga su software en las instrucciones de instalación y deje que el usuario lo instale manualmente. Puede que no suene tan emocionante para aquellos que quieren automatizar todo, pero sin duda es una forma más segura y, lo que es más importante, es obras.

    – hiobs

    17 de noviembre de 2011 a las 17:44

  • @skyhisi en realidad, para ARCHIVO (DESCARGAR… el segundo parámetro es archivo, por lo que no puede especificar una ruta.

    – Rodolfo

    13 de septiembre de 2013 a las 3:14


A partir de cmake 3.11 hay una nueva característica: FetchContent

Puede usarlo para obtener sus dependencias durante la configuración, por ejemplo, obtener el gran cmake-guiones.

include(FetchContent)

FetchContent_Declare(
  cmake_scripts
  URL https://github.com/StableCoder/cmake-scripts/archive/master.zip)
FetchContent_Populate(cmake_scripts)
message(STATUS "cmake_scripts is available in " ${cmake_scripts_SOURCE_DIR})

Prefiero obtener las fuentes comprimidas en lugar de verificarlas directamente. Pero FetchContent también permite definir un repositorio git.

  • Pero no resuelve las dependencias transitivas automáticamente.

    – seguir

    26 de agosto de 2021 a las 2:17

Avatar de usuario de Vertexwahn
vérticewahn

Dentro del universo CMake:

vcpkg

vcpkg es un administrador de paquetes para C++ Library Manager para Windows, Linux y macOS. Se puede integrar a la perfección con CMake; consulte aquí para detalles.

Conan

Conan es un administrador de paquetes C/C++. También tiene un estrategia para la integración con CMake.

CMake con ExternalProject_Add

CMakeList.txt.en:

cmake_minimum_required(VERSION 2.8.2)

project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
  GIT_REPOSITORY    https://github.com/google/googletest.git
  GIT_TAG           master
  SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
  BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
  CONFIGURE_COMMAND ""
  BUILD_COMMAND     ""
  INSTALL_COMMAND   ""
  TEST_COMMAND      ""
)

CMakeList.txt:

cmake_minimum_required(VERSION 3.8)

# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
  RESULT_VARIABLE result
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
if(result)
  message(FATAL_ERROR "CMake step for googletest failed: ${result}")
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
  RESULT_VARIABLE result
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
if(result)
  message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()

# Prevent overriding the parent project's compiler/linker
# settings on Windows
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This defines
# the gtest and gtest_main targets.
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
                 ${CMAKE_BINARY_DIR}/googletest-build)

# The gtest/gtest_main targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
  include_directories("${gtest_SOURCE_DIR}/include")
endif()

# Now simply link against gtest or gtest_main as needed. Eg
add_executable(example example.cpp)
target_link_libraries(example gtest_main)
add_test(NAME example_test COMMAND example)

ejemplo.cpp

#include <iostream>

#include "gtest/gtest.h"

TEST(sample_test_case, sample_test)
{
    EXPECT_EQ(1, 1);
}

Fuera del universo CMake:

¡Te sugiero que no uses CMake! Usar bazel!

Por ejemplo, si desea utilizar gtest:

ESPACIO DE TRABAJO

espacio de trabajo (nombre = “GTestDemo”)

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

git_repository(
    name = "googletest",
    #tag = "release-1.8.1",
    commit = "2fe3bd994b3189899d93f1d5a881e725e046fdc2",
    remote = "https://github.com/google/googletest",
    shallow_since = "1535728917 -0400",
)

CONSTRUIR

cc_test(
    name = "tests",
    srcs = ["test.cpp"],
    copts = ["-isystem external/gtest/include"],
    deps = [
        "@googletest//:gtest_main",
    ],

)

texto.cpp

#include <iostream>

#include "gtest/gtest.h"

TEST(sample_test_case, sample_test)
{
    EXPECT_EQ(1, 1);
}

¿Cómo ejecutar la prueba?

bazel test //...

Por ejemplo, si desea utilizar aumentar:

ESPACIO DE TRABAJO

workspace(name = "BoostFilesystemDemo")

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

# Fetch Boost repo
git_repository(
    name = "com_github_nelhage_rules_boost",
    commit = "49066b7ccafce2609a3d605e3667af3f07e8547c",
    remote = "https://github.com/Vertexwahn/rules_boost",
    shallow_since = "1559083909 +0200",
)

load("@com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps")

boost_deps()

CONSTRUIR

cc_binary(
    name = "FilesystemTest",
    srcs = ["main.cpp"],
    defines = ["BOOST_ALL_NO_LIB"],
    deps = [
        "@boost//:filesystem",
    ],
)

principal.cpp

#include <iostream>
#include <boost/filesystem.hpp>

using namespace boost::filesystem;

int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        std::cout << "Usage: tut1 path\n";
        return 1;
    }
    std::cout << argv[1] << " " << file_size(argv[1]) << '\n';
    return 0;
}

Cómo construir:

bazel build //...

Como correr:

bazel run //:FilesystemTest

Si desea generar una solución de Visual Studio, use lavanda. Desafortunadamente, la lavanda es solo experimental y necesita algunas mejoras. Pero creo que tiene más sentido esforzarse aquí en lugar de hacer que CMake funcione con todas sus dependencias. también hay algunos proyectos que intentan hacer una interoperabilidad Bazel CMake.

La mejor manera de lograr esto es eliminar sus dependencias.

Las dependencias son malas.

Elimínelos en lugar de depender de ellos.

En serio.

No desea descargarlos manualmente, no desea almacenarlos en su repositorio, sus clientes no desean descargarlos por usted. De hecho, su compilador ni siquiera quiere compilarlos.

Prefiero cambiar a Java para agregar una dependencia de la biblioteca C++…

Mientras tanto, la sugerencia de revisar el ExternoProyecto El módulo de CMake es lo más cerca que estará de una dependencia automática no almacenada en el repositorio, descarga, configuración, compilación e instalación en el corto plazo con una compilación basada en CMake.

¿Ha sido útil esta solución?