C++ CMake

O Make resolve o problema de recompilar só o que mudou, mas você ainda escreve cada regra à mão, lembra de cada .h, e o Makefile só funciona onde há make e g++. O Bruno, amigo da Ana, usa Windows com o Visual Studio; a Ana usa Linux com o VS Code. Como os dois compilam o mesmo projeto? A resposta da maioria dos projetos C++ do mundo é o CMake: você descreve o projeto num arquivo simples, o CMakeLists.txt, e o CMake gera os arquivos de compilação certos para cada sistema.

Você vai aprender
  • O que o CMake faz e por que ele é o padrão em projetos C++.
  • Um CMakeLists.txt mínimo, linha por linha.
  • Os dois passos: cmake -B build (configurar) e cmake --build build (compilar).
  • Como montar o projeto da lojinha, com vários arquivos.
  • Como usar o Ninja (-G Ninja) e outro compilador.
  • Os erros mais comuns do CMake e como lê-los.

Todos os comandos desta aula foram executados na máquina fictícia da Ana (usuária ana, computador notebook), com o CMake 4.2, o g++ 15.2 e o Ninja do Ubuntu. No Ubuntu, o CMake e o Ninja se instalam com sudo apt install cmake ninja-build. Para conferir:

$ cmake --version
cmake version 4.2.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).

A ideia: a planta da casa

Um arquiteto não diz ao pedreiro "assente 2.340 tijolos na posição tal". Ele desenha a planta: dois quartos, uma cozinha, um banheiro. Cada equipe de obra lê a planta e constrói do seu jeito, com as ferramentas que tem.

Na obraNo C++
A planta: "dois quartos e uma cozinha"o CMakeLists.txt: "um programa loja, feito de main.cpp e calculos.cpp, em C++23"
O mestre de obras, que transforma a planta em tarefaso CMake, que gera um Makefile, um projeto do Visual Studio, um arquivo do Ninja…
Os pedreiroso Make ou o Ninja, que chamam o compilador

Por isso o CMake é chamado de gerador de sistema de build (build = "construção", o processo de compilar e ligar). Ele não compila nada sozinho: ele escreve as instruções para quem compila.


1. O CMakeLists.txt mínimo

A Ana começou com um programa de um arquivo só, numa pasta ~/cpp/cm1:

#include <print>

int main() {
    std::println("Olá do CMake!");
}

Ao lado dele, ela criou um arquivo de texto com o nome exato CMakeLists.txt (com C, M e L maiúsculos):

cmake_minimum_required(VERSION 3.25)
project(Ola LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(ola ola.cpp)
cmake_minimum_required(VERSION 3.25)
"Versão mínima exigida do CMake". Todo CMakeLists.txt começa com ela. Além de recusar um CMake velho demais, ela diz ao CMake de que época é o seu arquivo, para ele se comportar como você espera. Use uma versão recente que você tenha (o Ubuntu 26.04 traz a 4.2; a 3.25 é de 2022 e já cobre tudo desta aula).
project(Ola LANGUAGES CXX)
O nome do projeto (Ola) e a linguagem. CXX é o nome que o CMake (e o Make) usa para C++. Sem o LANGUAGES CXX, o CMake também procuraria um compilador de C.
set(CMAKE_CXX_STANDARD 23)
set ("defina") dá valor a uma variável. Esta pede o C++23, como o -std=c++23 do comando padrão do guia.
set(CMAKE_CXX_STANDARD_REQUIRED ON)
"O padrão é obrigatório": se o compilador não souber C++23, pare com erro, em vez de cair silenciosamente para um padrão mais antigo.
add_executable(ola ola.cpp)
"Acrescente um executável": o programa vai se chamar ola e é feito do arquivo ola.cpp. Esse nome (ola) é o alvo (target), como no Make.

Os comandos do CMake são escritos como nome(argumentos), com os argumentos separados por espaço, sem vírgula e sem ;. Não diferenciam maiúsculas de minúsculas (ADD_EXECUTABLE também funciona; o costume atual é minúsculas), mas os nomes de variáveis, como CMAKE_CXX_STANDARD, sim.


2. Configurar e compilar

O CMake trabalha em dois passos. Primeiro, configurar: ler o CMakeLists.txt, procurar o compilador e gerar os arquivos de build numa pasta separada:

$ cd ~/cpp/cm1
$ cmake -B build
-- The CXX compiler identification is GNU 15.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.4s)
-- Generating done (0.0s)
-- Build files have been written to: /home/ana/cpp/cm1/build
-B build
A pasta onde os arquivos gerados vão ficar (B de build). Ela é criada se não existir. O CMakeLists.txt é procurado na pasta atual.
The CXX compiler identification is GNU 15.2.0
O CMake achou um compilador de C++: o GCC (o g++) 15.2. O /usr/bin/c++ é o nome genérico que, no Ubuntu, aponta para o g++.
Detecting… - done
Testes que o CMake faz para descobrir do que o compilador é capaz. Só rodam na primeira configuração.
Build files have been written to
"Os arquivos de build foram gravados em…". Pronto para compilar.

Depois, compilar:

$ cmake --build build
[ 50%] Building CXX object CMakeFiles/ola.dir/ola.cpp.o
[100%] Linking CXX executable ola
[100%] Built target ola
$ ./build/ola
Olá do CMake!

cmake --build build quer dizer "construa o que está configurado na pasta build". As linhas dizem o que está acontecendo: compilando (Building) o ola.cpp num .o, ligando (Linking) o executável, alvo pronto (Built target). O programa fica dentro da pasta build, por isso ./build/ola. Rodando de novo, sem mudar nada:

$ cmake --build build
[100%] Built target ola

Nada foi recompilado: por baixo, quem trabalhou foi o Make. Veja o que o CMake gerou na pasta build:

$ ls build
CMakeCache.txt
CMakeFiles
cmake_install.cmake
Makefile             « um Makefile, escrito pelo CMake
ola
Por que uma pasta build separada?

Tudo o que é gerado (o Makefile, os .o, o programa, o CMakeCache.txt com as escolhas da configuração) fica lá dentro, longe do seu código. Para recomeçar do zero, basta apagar a pasta: rm -rf build. E dá para ter várias ao mesmo tempo (uma com o g++, outra com o Clang), como você vai ver na seção 5. Se o projeto usa git, ponha build/ no .gitignore.

Quando reconfigurar?

Quase nunca à mão. Se você editar o CMakeLists.txt, o cmake --build percebe e reconfigura sozinho. A Ana tinha um programa que mostra o valor de __cplusplus (veja O Pré-processador), compilado com set(CMAKE_CXX_STANDARD 20), e trocou o 20 por 23:

$ ./build/qual
Compilador: GCC (g++) 15
__cplusplus = 202002
$ sed -i 's/STANDARD 20/STANDARD 23/' CMakeLists.txt
$ cmake --build build && ./build/qual
-- Configuring done (0.0s)                 « reconfigurou sozinho
-- Generating done (0.0s)
-- Build files have been written to: /home/ana/cpp/qual/build
[ 50%] Building CXX object CMakeFiles/qual.dir/qual.cpp.o
[100%] Linking CXX executable qual
[100%] Built target qual
Compilador: GCC (g++) 15
__cplusplus = 202302

(O sed -i trocou o texto dentro do arquivo; você pode fazer o mesmo num editor.)

Experimente: este é o programa qual.cpp da Ana. Esta caixa compila com -std=c++20, como um projeto com set(CMAKE_CXX_STANDARD 20). Rode e compare com a saída do g++ acima: aqui o compilador é o Clang, mas o padrão é o mesmo. Depois troque o 202002L do #if por 202302L e veja a outra mensagem aparecer.

#include <iostream>

int main() {
#if defined(__clang__)
    std::cout << "Compilador: Clang " << __clang_major__ << '\n';
#elif defined(__GNUC__)
    std::cout << "Compilador: GCC (g++) " << __GNUC__ << '\n';
#endif
    std::cout << "__cplusplus = " << __cplusplus << '\n';
#if __cplusplus >= 202002L
    std::cout << "Pelo menos C++20: pode usar std::format e ranges.\n";
#else
    std::cout << "Padrão antigo demais para este recurso.\n";
#endif
}

3. A lojinha com vários arquivos

Agora o projeto de verdade: main.cpp, calculos.cpp e calculos.h, os de Arquivos .h e .cpp, na pasta ~/cpp/loja-cmake:

cmake_minimum_required(VERSION 3.25)
project(Lojinha LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_executable(loja main.cpp calculos.cpp)
target_compile_options(loja PRIVATE -Wall -Wextra)
add_executable(loja main.cpp calculos.cpp)
Os .cpp do programa, separados por espaço. O .h não precisa entrar: o CMake descobre sozinho quais cabeçalhos cada .cpp inclui (o trabalho que você fazia à mão nas dependências do Makefile).
set(CMAKE_CXX_EXTENSIONS OFF)
Sem esta linha, o CMake pede ao g++ o -std=gnu++23: C++23 mais extensões próprias do GCC. Com ela, pede o -std=c++23 puro, igual ao do guia, e um código que usa extensões sem querer não passa despercebido.
target_compile_options(loja PRIVATE -Wall -Wextra)
Opções a mais para o alvo loja: os avisos de sempre (veja Avisos). PRIVATE quer dizer "só para compilar este alvo". Os comandos target_… sempre recebem primeiro o nome do alvo.
$ cd ~/cpp/loja-cmake
$ cmake -B build
-- The CXX compiler identification is GNU 15.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.6s)
-- Generating done (0.0s)
-- Build files have been written to: /home/ana/cpp/loja-cmake/build
$ cmake --build build
[ 33%] Building CXX object CMakeFiles/loja.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/loja.dir/calculos.cpp.o
[100%] Linking CXX executable loja
[100%] Built target loja
$ ./build/loja
Total: R$ 10
Com 10% de desconto: R$ 9

Faça o teste do touch da aula de Make. Mudando o cabeçalho, os dois .cpp são recompilados, mesmo sem o .h aparecer no CMakeLists.txt:

$ touch calculos.h
$ cmake --build build
[ 33%] Building CXX object CMakeFiles/loja.dir/calculos.cpp.o
[ 66%] Building CXX object CMakeFiles/loja.dir/main.cpp.o
[100%] Linking CXX executable loja
[100%] Built target loja

Ver os comandos de verdade: --verbose

Para ver o comando que o CMake mandou rodar, use --verbose ("falante"). A saída é comprida, cheia de detalhes internos do Make; aqui vão só as linhas que interessam:

$ touch calculos.cpp
$ cmake --build build --verbose
«… linhas internas do make omitidas …»
[ 33%] Building CXX object CMakeFiles/loja.dir/calculos.cpp.o
/usr/bin/c++   -std=c++23 -Wall -Wextra -MD -MT CMakeFiles/loja.dir/calculos.cpp.o -MF CMakeFiles/loja.dir/calculos.cpp.o.d -o CMakeFiles/loja.dir/calculos.cpp.o -c /home/ana/cpp/loja-cmake/calculos.cpp
[ 66%] Linking CXX executable loja
/usr/bin/c++ -Wl,--dependency-file=CMakeFiles/loja.dir/link.d CMakeFiles/loja.dir/main.cpp.o CMakeFiles/loja.dir/calculos.cpp.o -o loja
«…»
[100%] Built target loja

Repare: é o mesmo -std=c++23 -Wall -Wextra -c que você digitava, e depois a ligação dos .o, como em Compilar Vários Arquivos. As opções -MD -MT … -MF … pedem ao g++ que anote quais .h cada .cpp incluiu: é assim que o CMake sabe o que recompilar quando um cabeçalho muda. No projeto de um arquivo só, sem o CMAKE_CXX_EXTENSIONS OFF, a mesma linha tinha -std=gnu++23.

Uma biblioteca dentro do projeto

Em projetos maiores, as partes viram bibliotecas internas, que um ou mais programas usam:

cmake_minimum_required(VERSION 3.25)
project(Lojinha LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_library(calculos calculos.cpp)

add_executable(loja main.cpp)
target_link_libraries(loja PRIVATE calculos)
$ cmake -B build > /dev/null && cmake --build build && ./build/loja
[ 25%] Building CXX object CMakeFiles/calculos.dir/calculos.cpp.o
[ 50%] Linking CXX static library libcalculos.a
[ 50%] Built target calculos
[ 75%] Building CXX object CMakeFiles/loja.dir/main.cpp.o
[100%] Linking CXX executable loja
[100%] Built target loja
Total: R$ 10
Com 10% de desconto: R$ 9

add_library cria a biblioteca calculos (o arquivo libcalculos.a, um pacote de .o), e target_link_libraries diz que o loja precisa dela na ligação. (O > /dev/null só esconde as mensagens da configuração.) É o mesmo comando que você vai usar para ligar bibliotecas de terceiros em Usar Bibliotecas Externas.


4. Por que CMake?

g++ na mãoMakefileCMake
Recompila só o que mudounãosimsim
Descobre sozinho as dependências dos .hnãonão (você lista)sim
Funciona no Windows com Visual Studionãonãosim
Abre direto em IDEsnãopoucosim: VS Code (extensão CMake Tools), CLion, Visual Studio, Qt Creator
Bibliotecas de terceirosvocê passa -I, -lvocê passa -I, -lfind_package + target_link_libraries
Bom paraum ou dois arquivosprojetos pequenos, só Linuxqualquer projeto que vai crescer ou ser compartilhado

O mesmo CMakeLists.txt da Ana, no Windows do Bruno, gera um projeto do Visual Studio em vez de um Makefile; no Mac, gera para o Xcode ou para o Make. Quase todas as bibliotecas C++ conhecidas trazem um CMakeLists.txt, e as IDEs entendem esse arquivo: no VS Code com a extensão CMake Tools, abrir a pasta já oferece configurar e compilar (veja Editores e IDEs).


5. Ninja e outros geradores

O CMake pode gerar arquivos para vários "pedreiros". Esses formatos se chamam geradores, e se escolhem com -G. No Linux, o padrão é o Make (Unix Makefiles). Uma alternativa popular é o Ninja, um programa de build feito para ser rápido e silencioso:

$ cmake -B build-ninja -G Ninja
-- The CXX compiler identification is GNU 15.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.3s)
-- Generating done (0.0s)
-- Build files have been written to: /home/ana/cpp/loja-cmake/build-ninja
$ cmake --build build-ninja
[1/3] Building CXX object CMakeFiles/loja.dir/calculos.cpp.o
[2/3] Building CXX object CMakeFiles/loja.dir/main.cpp.o
[3/3] Linking CXX executable loja
$ ./build-ninja/loja
Total: R$ 10
Com 10% de desconto: R$ 9

O comando de compilar é o mesmo, cmake --build: ele chama o Make ou o Ninja, conforme a pasta. O Ninja mostra o progresso como [2/3] ("tarefa 2 de 3") e, por padrão, usa todos os núcleos do processador ao mesmo tempo, o que faz diferença em projetos grandes. Sem nada para fazer, ele diz:

$ cmake --build build-ninja
ninja: no work to do.
$ touch calculos.cpp
$ cmake --build build-ninja
[1/2] Building CXX object CMakeFiles/loja.dir/calculos.cpp.o
[2/2] Linking CXX executable loja

Outro compilador: CMAKE_CXX_COMPILER

Na configuração, -D dá valor a uma variável do CMake (como o -D do g++ define uma macro). Para usar o Clang numa terceira pasta:

$ cmake -B build-clang -G Ninja -DCMAKE_CXX_COMPILER=clang++
-- The CXX compiler identification is Clang 21.1.8
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/clang++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.5s)
-- Generating done (0.0s)
-- Build files have been written to: /home/ana/cpp/loja-cmake/build-clang
$ cmake --build build-clang
[1/3] Building CXX object CMakeFiles/loja.dir/calculos.cpp.o
[2/3] Building CXX object CMakeFiles/loja.dir/main.cpp.o
[3/3] Linking CXX executable loja

O compilador fica gravado no CMakeCache.txt daquela pasta. Para trocar, crie outra pasta (ou apague a antiga). Outra variável comum é -DCMAKE_BUILD_TYPE=Release (com otimizações) ou Debug (com informações para o GDB).


6. Quando dá errado

"Compatibility with CMake < 3.5 has been removed from CMake."

A Ana copiou um CMakeLists.txt de um tutorial antigo, que começava com cmake_minimum_required(VERSION 3.0):

$ cmake -B build
CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.5 has been removed from CMake.

  Update the VERSION argument <min> value.  Or, use the <min>...<max> syntax
  to tell CMake that the project requires at least <min> but has been updated
  to work with policies introduced by <max> or earlier.

  Or, add -DCMAKE_POLICY_VERSION_MINIMUM=3.5 to try configuring anyway.


-- Configuring incomplete, errors occurred!
$ echo $?
1

O que significa: CMake Error at CMakeLists.txt:1 (cmake_minimum_required) diz onde: linha 1, no comando cmake_minimum_required. O CMake 4 deixou de imitar as versões anteriores à 3.5 (de 2016). "Atualize o valor de VERSION."

Como resolver: troque para uma versão recente, como cmake_minimum_required(VERSION 3.25). Se o projeto não é seu e você só quer compilá-lo, a própria mensagem sugere cmake -B build -DCMAKE_POLICY_VERSION_MINIMUM=3.5.

"Cannot find source file"

Um erro de digitação no add_executable (calculo.cpp sem o s):

$ cmake -B build
-- The CXX compiler identification is GNU 15.2.0
«… detecções …»
-- Configuring done (0.3s)
CMake Error at CMakeLists.txt:4 (add_executable):
  Cannot find source file:

    calculo.cpp


CMake Error at CMakeLists.txt:4 (add_executable):
  No SOURCES given to target: loja


CMake Generate step failed.  Build files cannot be regenerated correctly.

O que significa: "Não encontrei o arquivo-fonte calculo.cpp", na linha 4. O segundo erro é consequência do primeiro: sem conseguir ler a lista, o alvo loja ficou "sem fontes" (No SOURCES).

Como resolver: confira o nome com ls e corrija o CMakeLists.txt. Sempre leia o primeiro erro primeiro.

Esqueceu um .cpp no add_executable

Com add_executable(loja main.cpp), a configuração passa (o CMake não sabe que faltou algo), mas a ligação falha:

$ cmake --build build
[ 50%] Building CXX object CMakeFiles/loja.dir/main.cpp.o
[100%] Linking CXX executable loja
/usr/bin/x86_64-linux-gnu-ld.bfd: CMakeFiles/loja.dir/main.cpp.o: na função "main":
main.cpp:(.text+0x1f): referência não definida para "calcularTotal(double, int)"
/usr/bin/x86_64-linux-gnu-ld.bfd: main.cpp:(.text+0x98): referência não definida para "aplicarDesconto(double, double)"
collect2: error: ld returned 1 exit status
gmake[2]: *** [CMakeFiles/loja.dir/build.make:101: loja] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:87: CMakeFiles/loja.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2

O que significa: é o undefined reference ("referência não definida") de Compilar Vários Arquivos: o calculos.cpp não entrou na ligação. As três linhas gmake no fim são o Make (o gmake é o GNU Make) avisando, em camadas, que a tarefa falhou. A causa está acima delas.

Como resolver: acrescente o arquivo: add_executable(loja main.cpp calculos.cpp).

"does not appear to contain CMakeLists.txt"
$ cmake -B build
CMake Error: The source directory "/home/ana/cpp/vazio" does not appear to contain CMakeLists.txt.
Specify --help for usage, or press the help button on the CMake GUI.

O que significa: "a pasta de código não parece conter um CMakeLists.txt". Você está na pasta errada, ou o arquivo tem outro nome (cmakelists.txt, CMakeList.txt).

Como resolver: cd para a pasta do projeto e confira o nome exato. Para indicar a pasta do código sem entrar nela, use -S: cmake -S ~/cpp/loja-cmake -B build.


Referência

No CMakeLists.txtO que faz
cmake_minimum_required(VERSION 3.25)versão mínima; sempre a primeira linha
project(Nome LANGUAGES CXX)nome do projeto e linguagem
set(CMAKE_CXX_STANDARD 23)padrão do C++
set(CMAKE_CXX_STANDARD_REQUIRED ON)o padrão é obrigatório
set(CMAKE_CXX_EXTENSIONS OFF)-std=c++23 em vez de -std=gnu++23
add_executable(prog a.cpp b.cpp)um programa e seus .cpp
add_library(nome x.cpp)uma biblioteca
target_link_libraries(prog PRIVATE nome)o programa usa a biblioteca
target_compile_options(prog PRIVATE -Wall -Wextra)opções de compilação do alvo
No terminalO que faz
cmake -B buildconfigura, gerando os arquivos em build
cmake -S pasta -B buildo mesmo, com o código em outra pasta
cmake -B build -G Ninjausa o Ninja em vez do Make
cmake -B build -DVAR=valordá valor a uma variável (CMAKE_CXX_COMPILER, CMAKE_BUILD_TYPE)
cmake --build buildcompila
cmake --build build --verbosecompila mostrando os comandos
cmake --build build --target cleanapaga o que foi compilado (como make clean)
rm -rf buildrecomeça do zero

Exercícios

1. O programa e seus arquivos

Complete para criar o programa estoque com os arquivos main.cpp e estoque.cpp:

(estoque main.cpp estoque.cpp)

2. Os dois passos

Complete o comando que compila um projeto já configurado na pasta build:

cmake

3. O que o cmake -B build faz?

4. A Ana acrescentou frete.cpp ao projeto, mas esqueceu de pô-lo no add_executable. O que acontece no cmake --build?

5. Para que serve -G Ninja?

Missão final: conserte o CMakeLists.txt da lojinha

Missão: o editor não roda o CMake, então a Ana escreveu um pequeno verificador em C++ (aba verificador.cpp) que lê a aba CMakeLists.txt e confere o que o CMake conferiria. A pasta do projeto tem main.cpp, calculos.cpp, calculos.h, frete.cpp e frete.h, e o main.cpp usa o frete. Rode, leia as reclamações e conserte só a aba CMakeLists.txt: o programa usa std::println (precisa de C++23), há um nome de arquivo errado e falta um arquivo. Quando estiver tudo certo, aparece [100%] Built target loja.

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

// Os arquivos que existem na pasta do projeto da Ana
const std::vector<std::string> pasta = {"main.cpp", "calculos.cpp", "calculos.h", "frete.cpp", "frete.h"};

bool estaNaPasta(const std::string& nome) {
    for (const std::string& a : pasta) {
        if (a == nome) {
            return true;
        }
    }
    return false;
}

int main() {
    std::ifstream arquivo("CMakeLists.txt");
    std::stringstream conteudo;
    conteudo << arquivo.rdbuf();
    std::string texto = conteudo.str();
    int erros = 0;

    if (texto.find("cmake_minimum_required(") == std::string::npos) {
        std::cout << "CMake Error: falta cmake_minimum_required(VERSION ...)\n";
        ++erros;
    }
    if (texto.find("project(") == std::string::npos) {
        std::cout << "CMake Error: falta project(...)\n";
        ++erros;
    }

    int padrao = 0;
    std::size_t p = texto.find("CMAKE_CXX_STANDARD ");
    if (p != std::string::npos) {
        std::istringstream(texto.substr(p + 19)) >> padrao;
    }
    if (padrao < 23) {
        std::cout << "error: 'println' is not a member of 'std' (o projeto pede C++"
                  << padrao << "; std::println precisa de set(CMAKE_CXX_STANDARD 23))\n";
        ++erros;
    }

    std::size_t inicio = texto.find("add_executable(");
    if (inicio == std::string::npos) {
        std::cout << "CMake Error: nenhum add_executable(...)\n";
        return 1;
    }
    inicio += 15;
    std::size_t fim = texto.find(')', inicio);
    std::istringstream lista(texto.substr(inicio, fim - inicio));
    std::string alvo, fonte;
    lista >> alvo;
    bool temMain = false, temCalculos = false, temFrete = false;
    while (lista >> fonte) {
        if (!estaNaPasta(fonte)) {
            std::cout << "CMake Error at CMakeLists.txt (add_executable):\n"
                      << "  Cannot find source file:\n\n    " << fonte << "\n\n";
            ++erros;
        }
        temMain = temMain || fonte == "main.cpp";
        temCalculos = temCalculos || fonte == "calculos.cpp";
        temFrete = temFrete || fonte == "frete.cpp";
    }
    if (erros > 0) {
        return 1;
    }
    if (!temMain) {
        std::cout << "ld: undefined reference to `main'\n";
        return 1;
    }
    if (!temCalculos || !temFrete) {
        if (!temCalculos) std::cout << "ld: undefined reference to `calcularTotal(double, int)'\n";
        if (!temFrete) std::cout << "ld: undefined reference to `calcularFrete(double)'\n";
        return 1;
    }
    std::cout << "-- Configuring done\n[100%] Built target " << alvo << '\n';
}
cmake_minimum_required(VERSION 3.25)
project(Lojinha LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(loja main.cpp calculo.cpp)
cmake_minimum_required(VERSION 3.25)
project(Lojinha LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(loja main.cpp calculos.cpp frete.cpp)

Resumo

  • O CMake é um gerador: lê o CMakeLists.txt e escreve os arquivos de build (Make, Ninja, Visual Studio…). Por isso o mesmo projeto compila no Linux, no Windows e no Mac, e abre nas IDEs.
  • Mínimo: cmake_minimum_required(VERSION 3.25), project(Nome LANGUAGES CXX), set(CMAKE_CXX_STANDARD 23) e add_executable(prog arquivos.cpp). Acrescente CMAKE_CXX_EXTENSIONS OFF e target_compile_options(prog PRIVATE -Wall -Wextra).
  • Dois passos: cmake -B build (configurar, uma vez) e cmake --build build (compilar, sempre). O programa fica em build/.
  • O CMake descobre sozinho os .h; liste só os .cpp. Esqueceu um: undefined reference. Nome errado: Cannot find source file.
  • -G Ninja troca o Make pelo Ninja; -DCMAKE_CXX_COMPILER=clang++ troca o compilador. Uma pasta de build para cada combinação.