Cmake vs make códigos de amostra?


117

Eu queria saber se existe algum código de amostra para Makefiles ( make) e CMakeLists.txt( cmake) que ambos fazem a mesma coisa (a única diferença é que um está escrito makee o outro cmake).

Eu tentei procurar por 'cmake vs make', mas nunca encontrei nenhuma comparação de código. Seria muito útil entender as diferenças, mesmo que apenas para um caso simples.


20
+1 Esta é uma boa pergunta; quando eu estava começando, cmakeeu também queria isso. Mas eu duvido que você o encontre porque os recursos simplesmente não mapeiam bem um ao outro. Se você tentar cmakeagir como se estivesse make, vai enlouquecer, sério. Melhor começar do zero. Coisas que são triviais makeestão bastante envolvidas cmakee vice-versa.
Ernest Friedman-Hill

1
@ErnestFriedman-Hill você tem mais detalhes sobre isso? Tão makee cmakesão tão distintos que deveriam ser vistos mais como ferramentas complementares em vez de ferramentas concorrentes?
Ehtesh Choudhury

2
@Shurane - cmake não constrói nada sozinho; ele cria Makefiles (e outros scripts de construção semelhantes), que você então executa. Portanto, sempre que estiver escrevendo arquivos cmake, você deve pensar se um comando deve ser aplicado durante o tempo de geração ou durante o tempo de compilação. Algumas ações - copiar um conjunto curinga de arquivos em tempo de construção, por exemplo - são bastante complicadas, em comparação com o " cp *.x $(OUTDIR)" que você escreveria em um Makefile. Talvez a parte mais irritante para mim é que os Makefiles gerados são por design completamente não portáveis ​​e inflexíveis (continuação)
Ernest Friedman-Hill

1
(continuação) Você não pode nem mesmo mover o diretório de origem na mesma máquina sem executar novamente o cmake para regenerar o Makefile! Portanto, a escolha não é entre cmake e make, mas sim entre escrever Makefiles portáteis você mesmo ou usar cmake para gerar arquivos não portáteis em cada máquina de construção (e dado que você pode usar Cygwin ou mingw no Windows, geralmente acho o primeiro mais fácil. )
Ernest Friedman-Hill

1
uma boa pergunta, mas não há uma resposta específica, porque ambas as ferramentas tentam resolver um problema diferente. cmake obtém informações sobre como construir programas e gera makefiles que constroem o programa. Portanto, cmake é uma linguagem com regras de construção abstratas e gnu make é uma resolução de dependência que executa programas em uma travessia de grafo acíclica direcionada.
Alex,

Respostas:


118

O seguinte Makefile constrói um executável nomeado a progpartir dos fontes prog1.c, prog2.c, prog3.c and main.c. progestá vinculado libmystatlib.a e libmydynlib.soque também são construídos a partir da fonte. Além disso, progusa a biblioteca libstuff.aem stuff/libe seu cabeçalho em stuff/include. O Makefile, por padrão, cria um destino de lançamento, mas também oferece um destino de depuração:

#Makefile    
CC = gcc
CPP = g++
RANLIB = ar rcs
RELEASE = -c -O3 
DEBUG = -c -g -D_DEBUG
INCDIR = -I./stuff/include
LIBDIR = -L./stuff/lib -L.
LIBS = -lstuff -lmystatlib -lmydynlib
CFLAGS = $(RELEASE)

PROGOBJS = prog1.o prog2.o prog3.o

prog: main.o $(PROGOBJS) mystatlib mydynlib
    $(CC) main.o $(PROGOBJS) $(LIBDIR) $(LIBS) -o prog 
debug: CFLAGS=$(DEBUG)
debug: prog

mystatlib: mystatlib.o
    $(RANLIB) libmystatlib.a mystatlib.o
mydynlib: mydynlib.o
    $(CPP) -shared mydynlib.o -o libmydynlib.so

%.o: %.c
    $(CC) $(CFLAGS) $(INCDIR) $< -o $@ 
%.o: %.cpp
    $(CPP) $(CFLAGS) $(INCDIR) -fPIC  $< -o $@ 

Aqui está um CMakeLists.txtque faz (quase) exatamente o mesmo, com alguns comentários para sublinhar as semelhanças com o Makefile:

#CMakeLists.txt     
cmake_minimum_required(VERSION 2.8)                    # stuff not directly
project(example)                                       # related to building

include_directories(${CMAKE_SOURCE_DIR}/stuff/include) # -I flags for compiler
link_directories(${CMAKE_SOURCE_DIR}/stuff/lib)        # -L flags for linker

set(PROGSRC prog1.c prog2.c prog3.c)                   # define variable 

add_executable(prog main.c ${PROGSRC})                 # define executable target prog, specify sources
target_link_libraries(prog mystatlib mydynlib stuff)   # -l flags for linking prog target

add_library(mystatlib STATIC mystatlib.c)              # define static library target mystatlib, specify sources

add_library(mydynlib SHARED mydynlib.cpp)              # define shared library target mydynlib, specify sources
#extra flags for linking mydynlib
set_target_properties(mydynlib PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 
#alternatively:
#set_target_properties(mydynlib PROPERTIES COMPILE_FLAGS "-fPIC")

Neste exemplo simples, as diferenças mais importantes são:

  • O CMake reconhece quais compiladores usar para cada tipo de fonte. Além disso, ele invoca a sequência correta de comandos para cada tipo de destino. Portanto, não há nenhuma especificação explícita de comandos como $(CC) ..., $(RANLIB) ...e assim por diante.

  • Todos os sinalizadores de compilador / vinculador usuais que lidam com a inclusão de arquivos de cabeçalho, bibliotecas, etc. são substituídos por comandos independentes de plataforma / sistema de compilação.

  • Bandeiras de depuração são incluídos por qualquer definindo a variável CMAKE_BUILD_TYPEde "depuração", ou passando-a para CMake quando invocando o programa: cmake -DCMAKE_BUILD_TYPE:STRING=Debug.

  • O CMake oferece também a inclusão independente de plataforma do sinalizador '-fPIC' (por meio da POSITION_INDEPENDENT_CODEpropriedade) e muitos outros. Ainda assim, configurações mais obscuras podem ser implementadas manualmente no CMake, assim como em um Makefile (usando COMPILE_FLAGS e propriedades semelhantes). É claro que o CMake realmente começa a brilhar quando bibliotecas de terceiros (como OpenGL) são incluídas de forma portátil.

  • O processo de construção tem uma etapa se você usar um Makefile, ou seja, digitar makena linha de comando. Para o CMake, há duas etapas: Primeiro, você precisa configurar seu ambiente de construção (digitando cmake <source_dir>no diretório de construção ou executando algum cliente GUI). Isso cria um Makefile ou algo equivalente, dependendo do sistema de compilação de sua escolha (por exemplo, make em Unixes ou VC ++ ou MinGW + Msys no Windows). O sistema de construção pode ser passado para CMake como um parâmetro; entretanto, o CMake faz escolhas padrão razoáveis, dependendo da configuração do seu sistema. Em segundo lugar, você executa a construção real no sistema de construção selecionado.

Fontes e instruções de construção estão disponíveis em https://github.com/rhoelzel/make_cmake .


3
O Makefile não é excessivamente complicado? Ao usar em CPPFLAGSvez de INCDIRum, poderia ter usado as regras embutidas e a invocação explícita do compilador teria sido redundante. Da mesma forma, para o manuseio de ar, as regras internas também podem abranger isso. Além disso, por que definir CPPe CCexplicitamente? Eles já estão configurados com bons valores por make, são variáveis ​​predefinidas. maketambém reconhece qual compilador usar para qual tipo de fonte, as regras internas são muitas.
Christian Hujer

1
E muitas dessas variáveis ​​devem ser atribuídas com em :=vez de =.
Christian Hujer

1
Olhando para a descrição, cmakeé mais comparável a do automakeque make.
ivan_pozdeev

O Makefile fornecido pode ser reduzido para 3/4 linhas. Você DEVE especificar em INCLUDESvez de INCDIR. Você não precisa de regras% .o:%. C.
shuva

6

Pegue algum software que use CMake como seu sistema de construção (há uma abundância de projetos de código aberto para escolher como exemplo). Obtenha o código-fonte e configure-o usando CMake. Leia os makefiles resultantes e divirta-se.

É preciso ter em mente que essas ferramentas não mapeiam um a um. A diferença mais óbvia é que o CMake procura dependências entre arquivos diferentes (por exemplo, cabeçalho C e arquivos de origem), enquanto o make deixa isso para os autores do makefile.


4

Se esta pergunta for sobre um exemplo de Makefilesaída do CMakeList.txtarquivo, verifique as fontes cmake-backend e gere um Makefile. Se não for, então adicionando à resposta de @Roberto, estou tentando torná-lo simples, escondendo os detalhes.

Função CMake

Embora Makeseja uma ferramenta flexível para regras e receitas, CMakeé uma camada de abstração que também adiciona o recurso de configuração.

Minha planície CMakeLists.txtse parecerá com o seguinte,

cmake_minimum_required(VERSION 2.8)
project(example)
file(GLOB testapp_SOURCES *.cc)
add_executable(testapp ${testapp_SOURCES})

Observe que isso CMakeoculta howa construção pode ser feito. Nós apenas especificamos whaté a entrada e a saída.

A CMakeLists.txtcontém a lista de chamadas de função que são definidas por cmake.

(Função CMake) Vs fazer regras

MakefileNo rules and recipessão usados em vez de functions. Além do functionrecurso -like, rules and recipesfornece encadeamento. Meu minimalista Makefileserá parecido com o seguinte,

-include "executable.mk"
TARGETS=testapp.bin
all:${TARGETS}

Embora o executable.mkseja parecido com o seguinte,

SOURCES=$(wildcard *.cpp)
OBJECTS=$(SOURCES:.cpp=.o)
DEPS=$(SOURCES:.cpp=.d)

%.bin:$(OBJECTS)
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) $(DEPS) $(TARGETS)

-include $(DEPS)

Começando do zero, devo começar com algo Makefilecomo o seguinte,

all: testapp.bin

testapp.bin:sourcea.o sourcb.o
    $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(LIBS)

.PHONY: all clean

clean:
    $(RM) $(OBJECTS) testapp.bin

Peguei esse snippet daqui e o modifiquei. Observe que algumas regras implícitas são adicionadas a este arquivo, as quais podem ser encontradas na documentação do makefile. Algumas variáveis ​​implícitas também são relevantes aqui.

Observe que isso Makefilefornece os detalhes recipemostrando que howa construção pode ser feita. É possível escrever executable.mkpara manter os detalhes definidos em um arquivo. Dessa forma, o makefile pode ser reduzido como mostrei anteriormente.

Variáveis ​​internas em CMakeeMake

Agora, ficando um pouco avançado, CMakepodemos definir um sinalizador de compilador como o seguinte,

set(CMAKE_C_FLAGS "-Wall")

Saiba mais sobre CMakeas variáveis ​​padrão no CMakeCache.txtarquivo. O CMakecódigo acima será equivalente ao Makecódigo abaixo,

CFLAGS = -Wall

Observe que CFLAGSé uma variável interna em Make, da mesma forma, CMAKE_C_FLAGSé uma variável interna em CMake.

adicionar incluir e caminho de biblioteca no CMake

Podemos fazer isso cmakeusando funções.

target_include_directories(testapp PRIVATE "myincludes")
list(APPEND testapp_LIBRARIES
    mytest mylibrarypath
)
target_link_libraries(testapp ${testapp_LIBRARIES})

Vs adicionar inclusão e caminho de biblioteca no Make

Podemos adicionar include e bibliotecas adicionando linhas como a seguinte,

INCLUDES += -Imyincludes
LIBS += -Lmylibrarypath -lmytest

Observe que as linhas acima podem ser geradas a partir de ferramentas de geração automática ou pkg-config. (embora o Makefile não dependa de ferramentas de configuração automática)

CMake configurar / ajustar

Normalmente é possível gerar algum config.harquivo como auto-configferramentas usando a configure_filefunção. É possível fazer mais truques escrevendo funções personalizadas. E, finalmente, podemos selecionar uma configuração como a seguinte,

cmake --build . --config "Release"

É possível adicionar alguma opção configurável usando a optionfunção.

Makefile configurar / ajustar

Se de alguma forma precisamos compilá-lo com algum sinalizador de depuração, podemos invocar o makesemelhante,

make CXXFLAGS=NDEBUG

Acho variáveis ​​internas, Makefile-rulese CMake-functionssão um bom começo para a comparação, boa sorte com mais escavações.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.