CMake: Harici projeler nasıl oluşturulur ve hedefleri nasıl dahil edilir?


114

Statik bir kitaplığı hedef olarak dışa aktaran bir Projem A var:

install(TARGETS alib DESTINATION lib EXPORT project_a-targets)
install(EXPORT project_a-targets DESTINATION lib/alib)

Şimdi Proje A'yı Proje B'den harici bir proje olarak kullanmak ve yerleşik hedefleri dahil etmek istiyorum:

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

Sorun, Proje B'nin CMakeLists'i çalıştırıldığında içerme dosyasının henüz mevcut olmamasıdır.

Dahil etmeyi inşa edilmekte olan harici projeye bağımlı hale getirmenin bir yolu var mı?

Güncelleme :Buna ve karşılaştığım diğer genel sorunlara dayalı olarak Örnek ile kısa bir CMake yazdım.

Yanıtlar:


67

Sanırım burada iki farklı paradigmayı karıştırıyorsunuz.

Sizin de belirttiğiniz gibi, oldukça esnek ExternalProjectmodül komutlarını derleme zamanında çalıştırır, bu nedenle Project A'nın içe aktarma dosyasını doğrudan kullanamazsınız çünkü bu dosya yalnızca Proje A kurulduktan sonra oluşturulur.

İsterseniz includeProje A'nın ithalat dosyası, sen edeceğiz sahip başka bir üçüncü taraf bağımlılık şekilde eklediğiniz veya üzeri gibi - elle Proje B'nin CMakeLists.txt çağırmadan önce Projesi A yüklemeye find_file/ find_library/ find_package.

Eğer faydalanmak istiyorsanız ExternalProject_Add, size CMakeLists.txt için aşağıdaki gibi bir şey eklemek gerekir:

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

ExternalProject_Get_Property(project_a install_dir)
include_directories(${install_dir}/include)

add_dependencies(project_b_exe project_a)
target_link_libraries(project_b_exe ${install_dir}/lib/alib.lib)

2
Cevabınız için teşekkürler. Önerdiğin şey daha önce sahip olduğuma benziyor.
Kitaplık

7
Kaynak ağacıma harici projelerin kaynaklarını dahil etmek zorunda kalmamak istedim. Tüm hedefler ExternalProject_Addgibi davranıp onları add_subdirectoryaçığa çıkarsak harika olurdu . Yukarıda tarif ettiğiniz çözüm muhtemelen hala en temiz olanıdır.
mirkokiefer

2
Bunları hem ExternalProject derlemeleri yapmayı ve ardından B'yi A'ya bağımlı hale getirmeyi düşünün ve ardından B projesi için CMakeLists dosyası, A projesindeki hedef dosyasını içerecektir, ancak "Süper Yapı" CMakeListleriniz, her ikisi de Harici Projeler olarak yalnızca A ve sonra B'yi oluşturacaktır. ...
DLRdave

3
@DLRdave - Super Build çözümünün birkaç kez önerildiğini gördüm, ancak yalnızca bazı harici projeler dahil olmak üzere ne gibi faydalar sağladığından emin değilim ExternalProject. Tutarlılık mı yoksa daha kanonik mi yoksa başka bir şey mi? Eminim burada temel bir şeyi kaçırıyorum.
Fraser

6
Bu çözümle ilgili sorunlardan biri, farklı işletim sistemleri paylaşılan kitaplıklar için farklı adlandırma şemaları kullandığından ve bu farklı adlandırmalara uyum sağladığından, derleme sistemini çapraz platform yapmayan kitaplık adını (alib.lib) kodlu hale getirmiş olmamızdır. şemaları, CMake'nin özelliklerinden biridir.
nsg

22

Bu gönderinin makul bir cevabı var:

CMakeLists.txt.in:

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      ""
)

CMakeLists.txt:

# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in
               googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )

# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This adds
# the following targets: gtest, gtest_main, gmock
# and gmock_main
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
                 ${CMAKE_BINARY_DIR}/googletest-build)

# The gtest/gmock 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"
                      "${gmock_SOURCE_DIR}/include")
endif()

# Now simply link your own targets against gtest, gmock,
# etc. as appropriate

Ancak oldukça hantal görünüyor. Alternatif bir çözüm önermek istiyorum - Git alt modülleri kullanın.

cd MyProject/dependencies/gtest
git submodule add https://github.com/google/googletest.git
cd googletest
git checkout release-1.8.0
cd ../../..
git add *
git commit -m "Add googletest"

Sonra da şöyle bir MyProject/dependencies/gtest/CMakeList.txtşey yapabilirsiniz:

cmake_minimum_required(VERSION 3.3)

if(TARGET gtest) # To avoid diamond dependencies; may not be necessary depending on you project.
    return()
endif()

add_subdirectory("googletest")

Bunu henüz kapsamlı bir şekilde denemedim ama daha temiz görünüyor.

Düzenleme: Bu yaklaşımın bir dezavantajı vardır: Alt dizin install(), istemediğiniz komutları çalıştırabilir . Bu gönderinin onları devre dışı bırakmak için bir yaklaşımı var, ancak buggy ve benim için çalışmadı.

Düzenleme 2: Kullanıyorsanız , alt dizindeki komutların varsayılan olarak kullanılmadığı add_subdirectory("googletest" EXCLUDE_FROM_ALL)anlamına gelir install().


Bu muhtemelen sadece aşırı temkinli davranıyorum çünkü bu sadece bir örnek ve gtest muhtemelen oldukça kararlı, ancak her zaman belirli bir GIT_TAGklonlama sırasında kullanmanızı şiddetle tavsiye ederim , derleme tekrarlanabilirliğini kaybedebilirsiniz çünkü bundan 2 yıl sonra derleme betiğini çalıştıran biri bir Yaptığınızdan farklı bir versiyon. CMake'nin dokümanları da bunu tavsiye ediyor.
JRH

5

Düzenleme: CMake artık bunun için yerleşik desteğe sahiptir. Yeni cevabı görün .

Ayrıca ikincil bir yapım sürecinde bağımlı hedefin oluşturulmasını zorlayabilirsiniz.

İlgili bir konuda cevabımı görün .


1

cmake ExternalProject_Addgerçekten de kullanılabilir, ama benim hoşuma gitmeyen şey - inşa sırasında, sürekli oylamada vb. bir şeyler yapmasıdır ... İnşa aşamasında proje oluşturmayı tercih ederim, başka bir şey değil. Ben geçersiz kılma çalıştık ExternalProject_Addmaalesef başarılı olamadı, birkaç denemeden içinde.

Sonra git alt modülünü de eklemeyi denedim, ancak bu tüm git deposunu sürüklüyor, bazı durumlarda ise tüm git deposunun yalnızca alt kümesine ihtiyacım var. Kontrol ettiğim şey - seyrek git checkout yapmak gerçekten mümkün, ancak bu aşağıda yazdığım ayrı bir işlev gerektiriyor.

#-----------------------------------------------------------------------------
#
# Performs sparse (partial) git checkout
#
#   into ${checkoutDir} from ${url} of ${branch}
#
# List of folders and files to pull can be specified after that.
#-----------------------------------------------------------------------------
function (SparseGitCheckout checkoutDir url branch)
    if(EXISTS ${checkoutDir})
        return()
    endif()

    message("-------------------------------------------------------------------")
    message("sparse git checkout to ${checkoutDir}...")
    message("-------------------------------------------------------------------")

    file(MAKE_DIRECTORY ${checkoutDir})

    set(cmds "git init")
    set(cmds ${cmds} "git remote add -f origin --no-tags -t master ${url}")
    set(cmds ${cmds} "git config core.sparseCheckout true")

    # This command is executed via file WRITE
    # echo <file or folder> >> .git/info/sparse-checkout")

    set(cmds ${cmds} "git pull --depth=1 origin ${branch}")

    # message("In directory: ${checkoutDir}")

    foreach( cmd ${cmds})
        message("- ${cmd}")
        string(REPLACE " " ";" cmdList ${cmd})

        #message("Outfile: ${outFile}")
        #message("Final command: ${cmdList}")

        if(pull IN_LIST cmdList)
            string (REPLACE ";" "\n" FILES "${ARGN}")
            file(WRITE ${checkoutDir}/.git/info/sparse-checkout ${FILES} )
        endif()

        execute_process(
            COMMAND ${cmdList}
            WORKING_DIRECTORY ${checkoutDir}
            RESULT_VARIABLE ret
        )

        if(NOT ret EQUAL "0")
            message("error: previous command failed, see explanation above")
            file(REMOVE_RECURSE ${checkoutDir})
            break()
        endif()
    endforeach()

endfunction()


SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_197 https://github.com/catchorg/Catch2.git v1.9.7 single_include)
SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_master https://github.com/catchorg/Catch2.git master single_include)

İşlevin nasıl kullanılacağını göstermek için aşağıya iki işlev çağrısı ekledim.

Biri ana / ana hattı ödünç almaktan hoşlanmayabilir, çünkü bu bozuk olabilir - o zaman belirli bir etiketi belirtmek her zaman mümkündür.

Önbellek klasörünü temizleyene kadar kontrol işlemi yalnızca bir kez gerçekleştirilecektir.


1

Ben de benzer bir çözüm arıyordum. Buradaki yanıtlar ve üstteki Eğitim bilgilendiricidir. Benimkini başarılı bir şekilde oluşturmak için burada belirtilen gönderileri / blogları inceledim. Tam CMakeLists.txt gönderiyorum benim için çalıştı. Sanırım bu, yeni başlayanlar için temel bir şablon olarak faydalı olacaktır.

"CMakeLists.txt"

cmake_minimum_required(VERSION 3.10.2)

# Target Project
project (ClientProgram)

# Begin: Including Sources and Headers
include_directories(include)
file (GLOB SOURCES "src/*.c")
# End: Including Sources and Headers


# Begin: Generate executables
add_executable (ClientProgram ${SOURCES})
# End: Generate executables


# This Project Depends on External Project(s) 
include (ExternalProject)

# Begin: External Third Party Library
set (libTLS ThirdPartyTlsLibrary)
ExternalProject_Add (${libTLS}
    PREFIX          ${CMAKE_CURRENT_BINARY_DIR}/${libTLS}
# Begin: Download Archive from Web Server
    URL             http://myproject.com/MyLibrary.tgz
    URL_HASH        SHA1=<expected_sha1sum_of_above_tgz_file>
    DOWNLOAD_NO_PROGRESS ON
# End: Download Archive from Web Server

# Begin: Download Source from GIT Repository
#    GIT_REPOSITORY  https://github.com/<project>.git
#    GIT_TAG         <Refer github.com releases -> Tags>
#    GIT_SHALLOW     ON
# End: Download Source from GIT Repository

# Begin: CMAKE Comamnd Argiments
    CMAKE_ARGS      -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${libTLS}
    CMAKE_ARGS      -DUSE_SHARED_LIBRARY:BOOL=ON
# End: CMAKE Comamnd Argiments    
)

# The above ExternalProject_Add(...) construct wil take care of \
# 1. Downloading sources
# 2. Building Object files
# 3. Install under DCMAKE_INSTALL_PREFIX Directory

# Acquire Installation Directory of 
ExternalProject_Get_Property (${libTLS} install_dir)

# Begin: Importing Headers & Library of Third Party built using ExternalProject_Add(...)
# Include PATH that has headers required by Target Project
include_directories (${install_dir}/include)

# Import librarues from External Project required by Target Project
add_library (lmytls SHARED IMPORTED)
set_target_properties (lmytls PROPERTIES IMPORTED_LOCATION ${install_dir}/lib/libmytls.so)
add_library (lmyxdot509 SHARED IMPORTED)
set_target_properties(lmyxdot509 PROPERTIES IMPORTED_LOCATION ${install_dir}/lib/libmyxdot509.so)

# End: Importing Headers & Library of Third Party built using ExternalProject_Add(...)
# End: External Third Party Library

# Begin: Target Project depends on Third Party Component
add_dependencies(ClientProgram ${libTLS})
# End: Target Project depends on Third Party Component

# Refer libraries added above used by Target Project
target_link_libraries (ClientProgram lmytls lmyxdot509)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.