En basit ama eksiksiz CMake örneği


117

Her nasılsa CMake'nin nasıl çalıştığı konusunda kafam karıştı. CMake'nin nasıl yazılması gerektiğini anlamaya her yaklaştığımı düşündüğümde, okuduğum bir sonraki örnekte kayboluyor. Tek bilmek istediğim, CMake'min gelecekte en az bakım gerektirmesi için projemi nasıl yapılandırmalıyım. Örneğin, src ağacıma tam olarak diğer src klasörleri gibi çalışan yeni bir klasör eklerken CMakeList.txt'mi güncellemek istemiyorum.

Projemin yapısını böyle hayal ediyorum ama lütfen bu sadece bir örnek. Önerilen yol farklıysa, lütfen bana söyleyin ve nasıl yapılacağını söyleyin.

myProject
    src/
        module1/
            module1.h
            module1.cpp
        module2/
            [...]
        main.cpp
    test/
        test1.cpp
    resources/
        file.png
    bin
        [execute cmake ..]

Bu arada, programımın kaynakların nerede olduğunu bilmesi önemli. Kaynakları yönetmenin önerilen yolunu öğrenmek istiyorum. Kaynaklarıma "../resources/file.png" ile erişmek istemiyorum


1
For example I don't want to update my CMakeList.txt when I am adding a new folder in my src treeKaynakları otomatik olarak toplayan bir IDE örneği verebilir misiniz?

7
hiçbir fikir normalde kaynakları otomatik olarak toplamaz, çünkü buna gerek yoktur. Yeni bir dosya veya klasör eklediğimde bunu ide dahilinde yapıyorum ve proje güncelleniyor. Diğer taraftaki bir yapı sistemi bazı dosyaları değiştirdiğimde fark etmiyor, bu nedenle tüm kaynak dosyalarını otomatik olarak toplaması istenen bir davranış
Arne

4
Bu bağlantıyı görürsem, CMake'nin çözmek istediği en önemli görevde başarısız olduğu izlenimine kapılıyorum: Çapraz platform oluşturma sistemini kolaylaştırmak.
Arne

Yanıtlar:


94

Biraz araştırmadan sonra, şimdi en basit ama eksiksiz bir cmake örneğinin kendi versiyonuna sahibim. İşte burada ve kaynaklar ve paketleme dahil olmak üzere temel bilgilerin çoğunu kapsamaya çalışıyor.

standart dışı yaptığı bir şey de kaynak kullanımıdır. Öntanımlı olarak cmake bunları / usr / share /, / usr / local / share / ve Windows'ta eşdeğer bir şey koymak ister. Her yerde ayıklayıp çalıştırabileceğiniz basit bir zip / tar.gz'ye sahip olmak istedim. Bu nedenle kaynaklar yürütülebilir dosyaya göre yüklenir.

cmake komutlarını anlamanın temel kuralı şu sözdizimidir: <function-name>(<arg1> [<arg2> ...])virgül veya semicolor olmadan. Her argüman bir dizedir. foobar(3.0)ve foobar("3.0")aynı. ile listeleri / değişkenleri ayarlayabilirsiniz set(args arg1 arg2). Bu değişken setiyle foobar(${args}) ve foobar(arg1 arg2)etkili bir şekilde aynıdır. Varolmayan bir değişken, boş bir listeye eşdeğerdir. Liste dahili olarak öğeleri ayırmak için noktalı virgül içeren bir dizedir. Bu nedenle, tek bir öğe içeren bir liste, tanım gereği yalnızca bu öğedir, kutulama gerçekleşmez. Değişkenler globaldir. Yerleşik işlevler , veya gibi bazı kimlikler bekledikleri için bir tür adlandırılmış bağımsız değişken sunar .PUBLICDESTINATIONargümanları gruplamak için argüman listesinde. Ancak bu bir dil özelliği değildir, bu kimlikler aynı zamanda sadece dizelerdir ve işlev uygulaması tarafından çözümlenir.

github'dan her şeyi klonlayabilirsiniz

cmake_minimum_required(VERSION 3.0)
project(example_project)

###############################################################################
## file globbing ##############################################################
###############################################################################

# these instructions search the directory tree when cmake is
# invoked and put all files that match the pattern in the variables 
# `sources` and `data`
file(GLOB_RECURSE sources      src/main/*.cpp src/main/*.h)
file(GLOB_RECURSE sources_test src/test/*.cpp)
file(GLOB_RECURSE data resources/*)
# you can use set(sources src/main.cpp) etc if you don't want to
# use globing to find files automatically

###############################################################################
## target definitions #########################################################
###############################################################################

# add the data to the target, so it becomes visible in some IDE
add_executable(example ${sources} ${data})

# just for example add some compiler flags
target_compile_options(example PUBLIC -std=c++1y -Wall -Wfloat-conversion)

# this lets me include files relative to the root src dir with a <> pair
target_include_directories(example PUBLIC src/main)

# this copies all resource files in the build directory
# we need this, because we want to work with paths relative to the executable
file(COPY ${data} DESTINATION resources)

###############################################################################
## dependencies ###############################################################
###############################################################################

# this defines the variables Boost_LIBRARIES that contain all library names
# that we need to link to
find_package(Boost 1.36.0 COMPONENTS filesystem system REQUIRED)

target_link_libraries(example PUBLIC
  ${Boost_LIBRARIES}
  # here you can add any library dependencies
)

###############################################################################
## testing ####################################################################
###############################################################################

# this is for our testing framework
# we don't add REQUIRED because it's just for testing
find_package(GTest)

if(GTEST_FOUND)
  add_executable(unit_tests ${sources_test} ${sources})

  # we add this define to prevent collision with the main
  # this might be better solved by not adding the source with the main to the
  # testing target
  target_compile_definitions(unit_tests PUBLIC UNIT_TESTS)

  # this allows us to use our executable as a link library
  # therefore we can inherit all compiler options and library dependencies
  set_target_properties(example PROPERTIES ENABLE_EXPORTS on)

  target_link_libraries(unit_tests PUBLIC
    ${GTEST_BOTH_LIBRARIES}
    example
  )

  target_include_directories(unit_tests PUBLIC
    ${GTEST_INCLUDE_DIRS} # doesn't do anything on Linux
  )
endif()

###############################################################################
## packaging ##################################################################
###############################################################################

# all install commands get the same destination. this allows us to use paths
# relative to the executable.
install(TARGETS example DESTINATION example_destination)
# this is basically a repeat of the file copy instruction that copies the
# resources in the build directory, but here we tell cmake that we want it
# in the package
install(DIRECTORY resources DESTINATION example_destination)

# now comes everything we need, to create a package
# there are a lot more variables you can set, and some
# you need to set for some package types, but we want to
# be minimal here
set(CPACK_PACKAGE_NAME "MyExample")
set(CPACK_PACKAGE_VERSION "1.0.0")

# we don't want to split our program up into several things
set(CPACK_MONOLITHIC_INSTALL 1)

# This must be last
include(CPack)

8
@SteveLorimer Sadece katılmıyorum, bu dosya globbing kötü bir stil, bence dosya ağacını CMakeLists.txt içine manuel olarak kopyalamak gereksiz çünkü kötü bir stil. Ancak insanların bu konu hakkında aynı fikirde olmadıklarını biliyorum, bu yüzden kodda bir yorum bıraktım, burada globbing'i tüm kaynak dosyaları açıkça içeren bir listeyle değiştirebilirsin. Arayın set(sources src/main.cpp).
Arne

3
@SteveLorimer evet sık sık cmake'yi tekrar çağırmak zorunda kaldım. Dizin ağacına her bir şey eklediğimde, cmake'ı manuel olarak yeniden çağırmam gerekir, böylece globbing yeniden değerlendirilir. Dosyaları içine koyarsanız, CMakeLists.txtnormal bir marka (veya ninja) cmake'nin yeniden çağrılmasını tetikler, böylece onu unutamazsınız. Aynı zamanda biraz takım dostudur, çünkü o zaman takım üyeleri de cmake'yi uygulamayı unutamazlar. Ama bir makefile'a sadece birisi dosya ekledi diye dokunulmasına gerek olmadığını düşünüyorum. Bir kez yazın ve kimsenin bir daha düşünmesine gerek kalmasın.
Arne

3
@SteveLorimer Projelerin her dizinine bir CMakeLists.txt koyma modeline de katılmıyorum, sadece projeyi her yere dağıtıyor, bence hepsini yapmak için bir dosya yeterli olmalı, yoksa genel bakışı kaybedersiniz. aslında inşa sürecinde yapılır. Bu, kendi CMakeLists.txt'lerine sahip alt dizinler olamayacağı anlamına gelmez, bunun bir istisna olması gerektiğini düşünüyorum.
Arne

2
"VCS" nin "versiyon kontrol sistemi " nin kısaltması olduğunu varsayarsak , o zaman bu alakasızdır. Sorun, yapay nesnelerin kaynak kontrolüne eklenmemesi değil. Sorun şu ki, CMake eklenen kaynak dosyaları yeniden değerlendiremiyor. Derleme sistemi girdi dosyalarını yeniden oluşturmaz. Derleme sistemi, ya hatalara yol açan (eğer şanslıysanız) ya da şansınız biterse fark edilmeyecek şekilde eski giriş dosyalarıyla mutlu bir şekilde yapışacaktır. GLOBbing, bağımlılık hesaplama zincirinde bir boşluk yaratır. Bu ise önemli bir sorun ve bir açıklama uygun bu kabul etmez.
2017

2
CMake ve bir VCS tam izolasyon içinde çalışır. VCS, CMake'nin farkında değildir ve CMake, herhangi bir VCS'nin farkında değildir. Aralarında bağlantı yok. Geliştiricilerin manuel adımlar atmasını, bilgileri VCS'den çıkarmasını ve CMake'yi sezgisel temizlemeye ve yeniden çalıştırmaya dayalı olmasını önermediğiniz sürece. Açıkçası bu ölçeklenmiyor ve insanlara özgü yanılgılara açık. Hayır, üzgünüm, şimdiye kadar GLOBbing dosyaları için ikna edici bir noktaya değinmediniz.
2017

39

En temel ancak eksiksiz örnek CMake eğitiminde bulunabilir :

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)

Proje örneğiniz için sahip olabilirsiniz:

cmake_minimum_required (VERSION 2.6)
project (MyProject)
add_executable(myexec src/module1/module1.cpp src/module2/module2.cpp src/main.cpp)
add_executable(mytest test1.cpp)

Ek sorunuz için, gitmenin bir yolu yine öğreticide: kodunuza ekleyeceğiniz yapılandırılabilir bir başlık dosyası oluşturun. Bunun için configuration.h.inaşağıdaki içeriğe sahip bir dosya oluşturun:

#define RESOURCES_PATH "@RESOURCES_PATH@"

Sonra CMakeLists.txteklediğinizde:

set(RESOURCES_PATH "${PROJECT_SOURCE_DIR}/resources/"
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/configuration.h.in"
  "${PROJECT_BINARY_DIR}/configuration.h"
)

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")

Son olarak, kodunuzda yola ihtiyaç duyduğunuz yerde şunları yapabilirsiniz:

#include "configuration.h"

...

string resourcePath = string(RESOURCE_PATH) + "file.png";

çok teşekkür ederim, özellikle RESOURCE_PATH için, nedense configure_file'ın aradığım şey olduğunu anlayamadım. Ancak projedeki tüm dosyaları manuel olarak eklediniz, tüm dosyaların src ağacından eklendiği bir kalıbı tanımlamanın daha iyi bir yolu var mı?
Arne

Dieter'in cevabına bakın, aynı zamanda onu neden kullanmamanız gerektiğine dair yorumlarıma da bakın. Gerçekten otomatikleştirmek istiyorsanız, daha iyi bir yaklaşım, kaynak dosyaların listesini yeniden oluşturmak için çalıştırabileceğiniz bir komut dosyası yazmak olabilir (veya bunu sizin için yapan bir cmake farkında IDE kullanın; hiçbirine aşina değilim).
sgvd

3
@sgvd string resourcePath = string(RESOURCE_PATH) + "file.png"IMHO , kaynak dizinin mutlak yolunu sabit kodlamak kötü bir fikirdir . Ya projenizi kurmanız gerekirse?

2
Kaynakları otomatik olarak toplamanın kulağa hoş geldiğini biliyorum, ancak her türden karmaşıklığa yol açabilir. Kısa bir tartışma için bir süre önceki bu soruya bakın: stackoverflow.com/q/10914607/1401351 .
Peter

2
Cmake komutunu çalıştırmazsanız tam olarak aynı hatayı alırsınız; elle dosya eklemek bir saniye sürer, her derlemede cmake çalıştırmak her seferinde bir saniye sürer; aslında bir cmake özelliğini kırarsınız; aynı proje üzerinde çalışan ve değişikliklerinizi uygulayan biri şunları yapacaktır: make'i çalıştırır -> tanımsız referansları alır -> umarım cmake'yi yeniden çalıştırmayı hatırlar veya sizinle dosya hatası verir -> cmake çalıştırır -> başarılı bir şekilde çalışır, oysa dosya eklerseniz elle yapar: koşular başarılı olur -> aileyle zaman geçirir. Özetle, tembel olmayın ve kendinizi ve başkalarını gelecekte baş ağrısından kurtarın.
sgvd

2

Burada en basit ama eksiksiz bir CMakeLists.txt dosyası örneği yazıyorum.

Kaynak kodu

  1. Merhaba dünyadan Android / iOS / Web / Masaüstü platformları arası öğreticiler.
  2. Her platformda bir örnek uygulama yayınladım.
  3. 08-cross_platform dosya yapısı çalışmam tarafından doğrulandı
  4. Mükemmel olmayabilir, ancak kendi başıma bir takım için faydalı ve en iyi uygulama olabilir

Daha sonra ayrıntılar için belge verdim.

Herhangi bir sorunuz varsa, benimle iletişime geçebilirsiniz ve size açıklamak istiyorum.

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.