Ero sivun ”CMake syvemmin” versioiden välillä

Linux.fista
Siirry navigaatioon Siirry hakuun
Ei muokkausyhteenvetoa
Rivi 15: Rivi 15:
== Ensimmäinen cmake-projekti ==
== Ensimmäinen cmake-projekti ==
make:n avulla pienen projektin kääntäminen:  
make:n avulla pienen projektin kääntäminen:  
Luo kansio cmakeprojekti, ja sinne tiedosto: '''CMakeLists.txt'''
Luo kansio cmakeprojekti, ja sinne tiedosto: <span style = "font-family:monospace; background-color:lightgray;" >CMakeLists.txt</span>
Esim. päätteellä:
Esim. päätteellä:
<syntaxhighlight lang="bash" line>
<syntaxhighlight lang="bash" line>

Versio 20. joulukuuta 2024 kello 18.10

CMake
Käyttöliittymä teksti
Lisenssi https://github.com/retifrav/cmake-cpack-example?tab=GPL-3.0-1-ov-file
Kotisivu cmake.org

CMaken käytön kuvaus

CMake on työkalu, jolla voidaan tehdä Makefilejä tai valmiita ohjelmistopaketteja. Se on siis Make-käskyä nähden pykälän verran abstraktimpi taso ylöspäin. Cmake:lla voidaan mm. tehdä deb- ja rpm-paketteja cpack-apuohjelman avulla. Lisäksi sen avulla voidaan hallita C/C++ kirjastoja joko valmiiksi käännettyjä moduleita tai luoda omia dynaamisia moduleita.

CMakeLists.txt on tiedosto, jonka perusteella cmake prosessoi ohjelmaprojektin. Ohjelmaprojekti voi olla esimerkiksi:testaus, ohjelman kääntäminen tai ohjelmapaketin luominen. CMakeLists.txt-tiedosto on siis yksi abstraktiotaso Makefile:stä abstraktimpaanpäin. CMakeLists.txt-tiedostot ovat käteviä erityisesti ohjelmoijille, jotka voivat niiden avulla automatisoida projektin Makefilejen ja ohjelmapakettien avulla.

CMakeLists.txt-tiedostossa voi olla monia käskyjä seka erittäin suuri määrä muuttuja-arvoja ja niitä voi itse lisätä. Tärkeimpiä käskyjä ovat esimerkiksi: cmake_minimum_required, project,set, install, include, if/else/elseif/endif, add_subdirectory, add_library, add_executable, target_link_libraries, configure_file, message, foreach, while/endwhile, function.

Kuten käskyistäkin voi päätellä, niin cmake on ohjelmointikieli. Koska siinä pystytään asettamaan muuttujia ja ehdollisia silmukoita, se täyttää kirkkaasti turingkoneen määritelmän. Tämä ohjelmointikieli on erikoistunut ohjelmien käännösten ja pakettien hallintaan. Kieli on case insensitive eli isolla ja pienellä kirjaimella ei ole eroa ohjelman prosessoinnissa. Varatut sanat saa selville käskyillä: cmake --help-command-list, cmake --help-variable-list ja cmake --help-property-list. Jos ajat käskyt, niin huomaat, että varattuja sanoja on todella monta. Esimerksiksi 3.28.3 versiossa on 1389-varattua sanaa. Onneksi kourallisella käskykannalla pääsee hyvin alkuun.

Cmake:n kotisivu on https://cmake.org/, josta löytyvät myös cmake-dokumentaatio. Cmake:n avulla käytetään myös erilaisia testaus-/paketoimis-ja käännösjärjestelmiä, kuten ctest, cpack, make, ninja jne. (joita muita kuin ctest:iä käytetään tässä esittelyssä).

Ensimmäinen cmake-projekti

make:n avulla pienen projektin kääntäminen: Luo kansio cmakeprojekti, ja sinne tiedosto: CMakeLists.txt Esim. päätteellä:

mkdir cmakeprojekti
cd cmakeprojekti
touch CMakeLists.txt

Kirjoita tiedostoon seuraavat rivit:

cmake_minimum_required(VERSION 3.21)
 
project(
    "eka-projekti"
    VERSION 0.1.0.2
    DESCRIPTION "Ensimmainen cmake-projektini"
    HOMEPAGE_URL "https://linux.fi"
    LANGUAGES "C" "CXX"
)
 
message("${PROJECT_NAME} on ensimmäinen projektimme, ja tässä on sen tiedot:")
message("Projektin versio on ${PROJECT_VERSION}")
message("Se koostuu neljästä osasta:")
message(${PROJECT_VERSION_MAJOR})
message(${PROJECT_VERSION_MINOR})
message(${PROJECT_VERSION_PATCH})
message(${PROJECT_VERSION_TWEAK})
message("Projektimme kuvaus on:${PROJECT_DESCRIPTION}")
message("Kotisivuksi olemme määritelleet tutun ${PROJECT_HOMEPAGE_URL}.")

Ensimmäinen rivi määrittelee, mikä cmake:n versio vähintään vaaditaan CMakeLists.txt:n suoritukseen. Project-käskyllä määritellään projektin nimi, versio, kuvaus,kotisivu ja ohjelmointikielet. Message-käskyllä tulostetaan käännösjonoon tekstiä. Tämän avulla voimme siis tulostaa projektimme tiedot. Rivillä 11 tulostamme käännösjonoon projektimme nimen, joka on nimeltään ”eka-projekti”. Projektin nimessä tulee olla vähintään yksi miinus (’-’)merkki. Seuraavasssa rivissä tulostamme projektin täyden version, joka siis on 0.1.0.2, kuten olimme project-käskyssä sen määritelleet. Seuraavat kolme message-käskyä kirjoittaa parsitun (järjestyksessä: MAJOR.MINOR.PATCH.TWEAK)versionumeron. Niiden jälkeen kirjoitetaan projektin kuvaus (PROJECT_DESCRIPTION). Viimeisessä message-rivissä tulostuu projektin kotisivu (PROJECT_HOMEPAGE_URL).

Nyt voimme ”kääntää” projektimme, joten luomme projektin juureen kansion build, ja ajamme cmake-ohjelman sieltä osoitettuna alempaan kansioon. (HUOM: ainakin cmake-paketti pitää olla asennettuna ennen ohjelman onnistunutta ajoa.)

mkdir build
cd build
cmake ..
make all

Ohjelman pitäisi tulostaa seuraavaa…

eka-projekti on ensimmäinen projektimme, ja tässä on sen tiedot:
Projektin versio on 0.1.0.2
Se koostuu neljästä osasta:
0
1
0
2
Projektimme kuvaus on:Ensimmainen cmake-projektini
Kotisivuksi olemme määritelleet tutun https://linux.fi.
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/.../cmakeprojekti/build

Huomioitavaa on, ettei make all-käsky kirjoita mitään, eikä se luo ajettavia tiedostoja. Toisaalta cmake-ohjelma ei myöskään anna mitään virheilmoituksia, niin siksi voimme määritellä tämän testin onnistuneeksi. Lisäksi on huomoitavaa, että muuttujien nimet (VERSION, DESCRIPTION, HOMEPAGE_URL ja LANGUAGES) on kirjoitettava suurilla kirjaimilla, jotta ne toimivat.

Ensimmäinen C/C++ ohjelma

Seuraavaksi voimmekin tehdä ensimmäisen käännettävän C/C++-ohjelman. Se kirjoitetaan oikeaoppisesti src-alikansioon, joten teemme sille projektikansiomme juureen uuden kansion. Päätteellä voisi kirjoittaa seuraavaa:

cd ..
mkdir src
touch src/hello.cpp

Ensimmäisessä rivissä siirrymme build-kansiosta projektin juurikansioon. Luomme sen alle src-kansion, jossa on hyvä pitää projektimme C/C++-lähdetiedostoja.

Kirjoita äsken src-hakemistoon luotuun hello.cpp-ohjelmaan seuraavan (Esimerkin) sisältö, ja tallenna se.

#include <iostream>
int main(int args,char **argv) {
    std::cout << "ohjelman ajotiedoston nimi hakemistopolkuineen on " << argv[0] << "\n";
    return 0;
}

Ohjelman voi kääntää käskyllä build-kansiostamme:

cd build
g++ ../src/hello.cpp -o hello

Ohjelman ajaminen tapahtuu seuraavasti:

 ./hello

Seuraavaksi menemme takaisin build-kansioon, ja poistamme cmake:n tekemät tiedostot rm -r -käskyllä (varovasti rm -r käskyn kanssa, sillä se voi tuhota pahimmassa tapauksessa koko käyttäjän kotikansion, ja root-käyttäjänä se voi tuhota koko järjestelmän.).

d ..
rm -r build
mkdir build
cd build
cmake ..
make all

Huomaa, että edelleenkin ohjelmat (cmake ja make) suoriutuvat toimistaan ilman virheilmoituksia, mutta edelleenkään tuotoksena ei tule käänettyä ohjelmaa.

Ajamisen jälkeen huomaamme, ettei cmake-käännä ohjelmaa, vaan se toimii edelleen samalla tavalla kuin aiemminkin eli tulostaa eka-projekti:n tiedot. Jotta saadaksemme cmake:n kääntämään ohjelman pitää meidän tehdä pieniä muutoksia. Lisäämme seuraavanlaisen rivin aikaisemmin luomamme CMakeLists.txt-tiedoston loppuun:

add_executable("hello" "src/hello.cpp")

Lisäyksen jälkeen CMakeLists.txt-tiedoston pitäisi näyttää tältä:

cmake_minimum_required(VERSION 3.21)

project(
    "eka-projekti"
    VERSION 0.1.0.2
    DESCRIPTION "Ensimmainen cmake-projektini"
    HOMEPAGE_URL "https://linux.fi"
    LANGUAGES "C" "CXX"
)

message("${PROJECT_NAME} on ensimmäinen projektimme, ja tässä on sen tiedot:")
message("Projektin versio on ${PROJECT_VERSION}")
message("Se koostuu neljästä osasta:")
message(${PROJECT_VERSION_MAJOR})
message(${PROJECT_VERSION_MINOR})
message(${PROJECT_VERSION_PATCH})
message(${PROJECT_VERSION_TWEAK})
message("Projektimme kuvaus on:${PROJECT_DESCRIPTION}")
message("Kotisivuksi olemme määritelleet tutun ${PROJECT_HOMEPAGE_URL}.")

add_executable("hello" "src/hello.cpp")

Nyt voimmekin ajaa cmake ja make all -ohjelmat build-hakemistostamme eli päätteessä (varmuuden vuoksi tässä poistetaan aiemmin luotu build-kansion sisältö. Todennäköisesti mkdir build antaa virheilmoituksen, kun kyseinen hakemisto on jo olemassa):

cd ..
mkdir build
cd build
rm -r *
cmake ..
make all

Komentosarjan pitäisi olla vastaavaa kuin tämä:

-- The C compiler identification is GNU 13.2.0
-- The CXX compiler identification is GNU 13.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- 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
eka-projekti on ensimmäinen projektimme, ja tässä on sen tiedot:
Projektin versio on 0.1.0.2
Se koostuu neljästä osasta:
0
1
0
2
Projektimme kuvaus on:Ensimmainen cmake-projektini
Kotisivuksi olemme määritelleet tutun https://linux.fi.
-- Configuring done (0.6s)
-- Generating done (0.0s)
-- Build files have been written to: /home/.../cmakeprojekti/build

Sitten cmake-ohjelman ajamisen jälkeen ajamme make-ohjelman (make all).

make all
[ 50%] Building CXX object CMakeFiles/hello.dir/src/hello.cpp.o
[100%] Linking CXX executable hello
[100%] Built target hello

Nyt ohjelma on käännetty, ja sen pitäisi tulostaa seuraavaa, kun ohjelman ajaa...

./hello
ohjelman ajotiedoston nimi hakemistopolkuineen on ./hello

Vielä emme siis ole päässeet nauttimaan cmake:n erityisen hienoista ominaisuuksista, mutta ensimmäisen C/C++-ohjelman onnistuimme kuitenkin kääntää ajettavaksi ohjelmaksi.

Cmake-ohjelman mallien käyttäminen

C++/C-ohjelmalle mallin luominen cmake:n ja CMakeLists.txt:n avulla. Ensimmäisessä esimerkissämme tulostimme message-käskyllä projektin tiedot tulostusjonoon. Nyt lisäämme kyseiset tiedot ohjelmamme lähdekoodeihin, jotka sitten tulostamme ajettavassa ohjelmassa. Luomme projektimme juureen cmakemallit-kansion, johon kirjoitamme cmake-kielellä pohjan käännettäävää ohjelmaa varten. Annamme tiedostonnimeksi asetukset.cmake.

Luo projektikansiomme juureen cmake-kansio, johon luo config.h.cmake-tiedosto. Siihen kirjoita seuraava sisältö:

#ifndef CONFIG_H
#define CONFIG_H

#define PROJECT "@PROJECT_NAME@"
#define PROJECT_VERSION "@PROJECT_VERSION@"
#define PROJECT_VERSION_MAJOR "@PROJECT_VERSION_MAJOR@"
#define PROJECT_VERSION_MINOR "@PROJECT_VERSION_MINOR@"
#define PROJECT_VERSION_PATCH "@PROJECT_VERSION_PATCH@"
#define PROJECT_VERSION_TWEAK "@PROJECT_VERSION_TWEAK@"
#define PROJECT_HOMEPAGE_URL "@PROJECT_HOMEPAGE_URL@"
#define PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@"

#endif

Malli on siis lähes samanlainen kuin puhdaskin C-otsaketiedosto. ”@...@”-välissä määritellyn cmake-muuttujan cmake muuttaa saman muuttujan arvoksi ennen C-kielisen ohjelman kääntämistä. Nämä ovat siis samat arvot kuin CMakeLists.txt-tiedoston message-käskyllä annetuissa muuttujissa. Tosin CMakeLists.txt-tiedostoon pitää lisätä käsky, jotta cmake osaa kirjoittaa header-tiedoston oikealla nimellä oikeaan paikkaan. Käsky, joka pitää lisätä on configure_file, ja tarkemmin:

configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.cmake ${CMAKE_SOURCE_DIR}/src/config.h)

se pidän loogisena, että sen paikka on ennen add_executable-käskyä, mutta se saattaa toimia myös sen jälkeen. Tämän muutoksen jälkeen CMakeLists.txt-tiedosto on kokonaisuudessaan seuraavan lainen …

cmake_minimum_required(VERSION 3.21)

project(
    "eka-projekti"
    VERSION 0.1.0.2
    DESCRIPTION "Ensimmainen cmake-projektini"
    HOMEPAGE_URL "https://linux.fi"
    LANGUAGES "C" "CXX"
)

message("${PROJECT_NAME} on ensimmäinen projektimme, ja tässä on sen tiedot:")
message("Projektin versio on ${PROJECT_VERSION}")
message("Se koostuu neljästä osasta:")
message(${PROJECT_VERSION_MAJOR})
message(${PROJECT_VERSION_MINOR})
message(${PROJECT_VERSION_PATCH})
message(${PROJECT_VERSION_TWEAK})
message("Projektimme kuvaus on:${PROJECT_DESCRIPTION}")
message("Kotisivuksi olemme määritelleet tutun ${PROJECT_HOMEPAGE_URL}.")

configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.cmake ${CMAKE_SOURCE_DIR}/src/config.h)

add_executable("hello" "src/hello.cpp")

Elikkä configure_file-käskyllä lisätään config.h.cmake-tiedoston ohjeiden mukaisesti projektin tiedot C-ystävälliseen muotoon #define-käskyjen muodossa. ${CMAKE_SOURCE_DIR} on lähdekoodin polku, /cmake/config.h.cmake-on lähdekoodipolussa olevan tiedoston nimi. /src/config.h on lähdekoodin (koko)polku, jonne asetustiedosto (config.h) lisätään.

Tämän jälkeen voit testata onnistuuko kääntäminen.

d ..
mkdir build
cd build
rm -r *
cmake ..
make all

Kääntämisen jälkeen ./hello-ohjelman pitäisi toimia samalla tavalla kuin ennen, mutta jos huomaat, niin projektin src-hakemistoon ilmestyi uusi config.h-tiedosto. Jotta saisimme konkreettista hyötyä mallitiedostosta, niin sen generoima otsaketiedosto kannattaa lisätä hello.cpp ohjelmaan #include-käskyllä: #include ”config.h”. Tämän jälkeen hello.cpp-tiedostomme on siis seuraavan mukainen…

#include <iostream>
#include "config.h"

int main(int args,char **argv) {
    std::cout << "ohjelman ajotiedoston nimi hakemistopolkuineen on " << argv[0] << "\n";
    std::cout << PROJECT;
    std::cout << "\n" << PROJECT_VERSION ;
    std::cout << "\n" << PROJECT_VERSION_MAJOR ;
    std::cout << "\n" << PROJECT_VERSION_MINOR;
    std::cout << "\n" << PROJECT_VERSION_PATCH;
    std::cout << "\n" << PROJECT_VERSION_TWEAK;
    std::cout << "\n" << PROJECT_HOMEPAGE_URL;
    std::cout << "\n" << PROJECT_DESCRIPTION;
    std::cout << "\n";

    return 0
}

Tiedosto puu (ilman build-kansiota) on seuraavannäköinen…

$ tree cmakeprojekti
cmakeprojekti
├── cmake
│   └── config.h.cmake
├── CMakeLists.txt
└── src
    ├── config.h
    └── hello.cpp

3 directories, 4 files

Tämän jälkeen voitkin koeajaa ohjelman:

cd ..
rm -r build
mkdir build
cd build
cmake ..
make all
./hello

./hello:n pitäisi tulostaa seuraavaa…

eka-projekti
0
1
0
2
https://linux.fi
Ensimmäinen cmake-projektini

Oman kirjaston lisääminen (.h)

Cmake:lla voi määritellä ja kääntää omia kirjastoja joko lähdeohjelmien kanssa tai pelkän käännöksen ja otsaketiedostojen (.h) avulla. Lisäämme itse tekemämme kirjaston, joka palauttaa liukulukujen välisen kertolaskun tuloksen palautteena.

Siispä lisäämme lib-hakemiston kirjaston lähdekoodia varten ja include-hakemiston otsaketiedostoja varten. Päätteellä se tapahtuu seuraavalla tavalla, mikäli olet päätteellä projektin build-hakemistossa.

mkdir ../lib;touch ../lib/mylib.cpp
mkdir ../include;touch ../include/mylib.h

Tämän jälkeen projektin hakemistopuu näyttää tältä:

cmakeprojekti
├── build
├── cmake
│   └── config.h.cmake
├── CMakeLists.txt
├── include
│   └── mylib.h
├── lib
│   └── mylib.cpp
└── src
    ├── config.h
    └── hello.cpp

Kirjoita mylib.h-tiedostoon seuraavanlainen otsaketiedosto:

#ifndef MYLIB_H
#define MYLIB_H
double multiply(double v1,double v2);
#endif

Vastaavasti kirjoita mylib.cpp-tiedostoon seuraavanlainen ohjelmasisältö:

#include "mylib.h"

double multiply(double v1,double v2) { 
    // multiply-funktion toteutus.
    // Palauttaa v1:n ja v2:n välisen kertolaskun tuloksen.
    return v1*v2;
}

Jotta voimme testata ohjelman toimintaa, niin meidän pitää tehdä muutoksia (hello.cpp) pääohjelmaamme. Lisäämme #include <mylib.h> ja rivit ohjelman testaamista varten.

Muutosten jälkeen hello.cpp-näyttää tällaiselta:

#include <iostream>
#include "config.h"
#include <mylib.h>

int main(int args,char **argv) {
    std::cout << "ohjelman ajotiedoston nimi hakemistopolkuineen on " << argv[0] << "\n";
    std::cout << PROJECT;
    std::cout << "\n" << PROJECT_VERSION ;
    std::cout << "\n" << PROJECT_VERSION_MAJOR ;
    std::cout << "\n" << PROJECT_VERSION_MINOR;
    std::cout << "\n" << PROJECT_VERSION_PATCH;
    std::cout << "\n" << PROJECT_VERSION_TWEAK;
    std::cout << "\n" << PROJECT_HOMEPAGE_URL;
    std::cout << "\n" << PROJECT_DESCRIPTION;
    std::cout << "\n" << multiply(3,5);
    if(args>2) {
      std::cout << "\n" << multiply(atof(argv[1]) ,atof(argv[2]));
    }
    std::cout << "\n";
}

Huomioi uudet rivit: 3,15-18 !


Edelliseen verrattuna ohjelmaan on tullut uusi #include-rivi #include-rivien viimeiseksi, jossa otetaan käyttöön oma tekemä mylib-kirjasto. Sen lisäksi mylib-kirjastossa olevaa multiply-funktiota testataan vakioilla, ja jos ohjelmalle syötetään parametreja kaksi tai enemmän, niin ohjelma yrittää muuttaa niistä kahta ensimmäista numeroksi ja kertoa ne keskenään. Muutokset ovat korostettu keltaisella taustavärillä.

Tämän osion lopuksi teemme tarvittavat muutokset CMakeLists.txt-tiedostoon, tiedostoon lisätään seuraavat rivit configure_file-käskyn jälkeen:

add_library(mylib lib/mylib.cpp)
target_include_directories(mylib PUBLIC include)

add_library-käskyllä lisätään projektiin mylib.cpp-kirjasto käännettäväksi, ja target_include_directories-käskyllä laitetaan include-hakemisto näkyväksi C/C++-kääntäjälle, josta kääntäjä osaa hakea (.h) header-tiedostot. CMakeLists.txt-tiedoston loppuun lisätään vielä rivi, jolla kirjasto linkitetään projektin hello.cpp-ohjelmaan.


target_link_libraries(hello mylib )

Tämän jälkeen CMakeLists.txt-tiedoston pitäisi näyttää tältä:

cmake_minimum_required(VERSION 3.21)

project(
    "eka-projekti"
    VERSION 0.1.0.2
    DESCRIPTION "Ensimmainen cmake-projektini"
    HOMEPAGE_URL "https://linux.fi"
    LANGUAGES "C" "CXX"
)

message("${PROJECT_NAME} on ensimmäinen projektimme, ja tässä on sen tiedot:")
message("Projektin versio on ${PROJECT_VERSION}")
message("Se koostuu neljästä osasta:")
message(${PROJECT_VERSION_MAJOR})
message(${PROJECT_VERSION_MINOR})
message(${PROJECT_VERSION_PATCH})
message(${PROJECT_VERSION_TWEAK})
message("Projektimme kuvaus on:${PROJECT_DESCRIPTION}")
message("Kotisivuksi olemme määritelleet tutun ${PROJECT_HOMEPAGE_URL}.")

configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.cmake ${CMAKE_SOURCE_DIR}/src/config.h)

add_library(mylib lib/mylib.cpp)
target_include_directories(mylib PUBLIC include)

add_executable("hello" "src/hello.cpp")

target_link_libraries(hello mylib )

Oman hello-ohjelman uusien ominaisuuksien kokeilu

Ensin käännetään ohjelma: Mene päätteellä projektin juureen, ja suorita alla olevat käskyt. (Sinun tulee tietää, mitä käskyt tekevät, koska väärässä kansiossa annettuna nämä käskyt saattavat tehdä arvaamatonta tuhoa):

mkdir build
cd build
rm -r *
cmake ..
make all

Suoritusten jälkeen sinulla pitäisi olla valmiiksi käännettynä hello-ohjelma, jota voit testata esimerkiksi seuraavalla tavalla:

./hello
./hello 5 9

Ohjelma tulostaneen seuraavan kaltaista:

ohjelman ajotiedoston nimi hakemistopolkuineen on ./hello
eka-projekti
0.1.0.2
0
1
0
2
https://linux.fi
Ensimmainen cmake-projektini
15
ohjelman ajotiedoston nimi hakemistopolkuineen on ./hello
eka-projekti
0.1.0.2
0
1
0
2
https://linux.fi
Ensimmainen cmake-projektini
15
45

Tällä hetkellä puun pitäisi näyttää tältä:

cmakeprojekti
├── cmake
│   └── config.h.cmake
├── CMakeLists.txt
├── include
│   └── mylib.h
├── lib
│   └── mylib.cpp
└── src
    ├── config.h
    └── hello.cpp

Ulkoisen kirjaston käyttäminen

Ulkoisena testikirjastona käytämme boost-kirjastoa.

Boost-kirjaston asentaminen Debian-pohjaisissa ympäristöissä

Ohjelman asennus tapahtuu yksinkertaisesti päätteellä:

sudo apt install libboost-all-dev

(Muiden ympäristöjen käyttäjät tietänevät asennuskäskyn minua paremmin, joten en niitä ala arvuuttelemaan.)

CMakeLists.txt:n muutokset Boost-kirjaston käyttöön

Muokkaamme CMakeLists.txt-tiedoston riviä:

target_link_libraries(hello mylib boost_date_time)

Lisäämme find_package(boost_date_time REQUIRED) rivin message-rivien perään.

find_package(boost_date_time REQUIRED)

joten CMakeLists.txt-tiedosto on kokonaisuudessaan seuraavan näköinen:

cmake_minimum_required(VERSION 3.21)

project(
    "eka-projekti"
    VERSION 0.1.0.2
    DESCRIPTION "Ensimmainen cmake-projektini"
    HOMEPAGE_URL "https://linux.fi"
    LANGUAGES "C" "CXX"
)

message("${PROJECT_NAME} on ensimmäinen projektimme, ja tässä on sen tiedot:")
message("Projektin versio on ${PROJECT_VERSION}")
message("Se koostuu neljästä osasta:")
message(${PROJECT_VERSION_MAJOR})
message(${PROJECT_VERSION_MINOR})
message(${PROJECT_VERSION_PATCH})
message(${PROJECT_VERSION_TWEAK})
message("Projektimme kuvaus on:${PROJECT_DESCRIPTION}")
message("Kotisivuksi olemme määritelleet tutun ${PROJECT_HOMEPAGE_URL}.")

find_package(boost_date_time REQUIRED)

configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.cmake ${CMAKE_SOURCE_DIR}/src/config.h)
add_library(mylib lib/mylib.cpp)
target_include_directories(mylib PUBLIC include)
add_executable("hello" "src/hello.cpp")
target_link_libraries(hello mylib boost_date_time)

Projektiin lisättävät rivit

Pääohjelmaan src/hello.cpp on lisättävä include rivi:

#include  <boost/date_time/posix_time/posix_time.hpp>

ja ajan tulostamisrivit ohjelman loppupuolelle:

boost::posix_time::ptime time = boost::posix_time::second_clock::local_time();
    std::cout << "Aika on " << time << "\n";

Tämän jälkeen src/hello.cpp-tiedoston pitäisi näyttää tältä:

#include <iostream>
#include "config.h"
#include <mylib.h>
#include <boost/date_time/posix_time/posix_time.hpp>

int main(int args,char **argv) {
    std::cout << "ohjelman ajotiedoston nimi hakemistopolkuineen on " << argv[0] << "\n";
    std::cout << PROJECT;
    std::cout << "\n" << PROJECT_VERSION ;
    std::cout << "\n" << PROJECT_VERSION_MAJOR ;
    std::cout << "\n" << PROJECT_VERSION_MINOR;
    std::cout << "\n" << PROJECT_VERSION_PATCH;
    std::cout << "\n" << PROJECT_VERSION_TWEAK;
    std::cout << "\n" << PROJECT_HOMEPAGE_URL;
    std::cout << "\n" << PROJECT_DESCRIPTION;
    std::cout << "\n" << multiply(3,5);
    if(args>2) {
        std::cout << "\n" << multiply(atof(argv[1]),atof(argv[2]));
    }
    std::cout << "\n";
    boost::posix_time::ptime time = boost::posix_time::second_clock::local_time();
    std::cout << "Aika on " << time << "\n";
    return 0;
}

Ohjelman kääntäminen ja koeajaminen

Ohjelma käännetään tuttuun tapaan projektin juurikansiosta seuraavalla tutulla käskysarjalla:

mkdir build
cd build
rm -r *
cmake ..
make all

Ohjelma ajetaan tutulla käskyllä:

./hello

Käsky tulostaa jotain seuraavaa...

eka-projekti
0.1.0.2
0
1
0
2
https://linux.fi
Ensimmainen cmake-projektini
15
Aika on 2024-Dec-04 20:10:41

Deb/rpm-paketin tekeminen cmake/cpack yhdistelmällä

Huom! Tämän sisältö on ottanut erittäin paljon vaikutteita näistä kahdesta artikkelista:

https://karthikkalyanaraman.medium.com/creating-debian-packages-cmake-e519a0186e87

ja varsinkin

https://decovar.dev/blog/2021/09/23/cmake-cpack-package-deb-apt/

Jos englanti taipuu, niin kannattaa tutustua ainakin jälkimmäiseen, sillä kyseinen artikkeli on tätä artikkelia monipuolisempi.

Ehkä lyhin tapa tehdä debian paketti (ilman riippuvuuksia) on lisätä CMakeLists.txt-tiedoston loppuun:

# CPACK:n tarvitsemat käskyt
install(TARGETS hello ) # Tiedoston lisäys pakettiin.

set(CPACK_DEBIAN_PACKAGE_MAINTAINER "peran") # Paketin ylläpitäjä

#Rpm-paketin myyjä, jonka laitoin samaksi kuin deb:n ylläpitäjä.
set(CPACK_RPM_PACKAGE_VENDOR ${CPACK_DEBIAN_PACKAGE_MAINTAINER})

include(CPack) # Tiedot cpack-ohjelmalle.

Minimalistisen Debian-paketin luomisen testaaminen

Mene build-hakemistoon päätteessä: (cd build).

rm -r *
cmake ..
cpack -G DEB

Nyt näemme ensimmäisen itse tekemäsi debian-paketin päätteen käskyllä !!!

ls -l *.deb
-rw-rw-r-- cmaker cmaker 66416 joulu   9 18:19 eka-projekti-0.1.0-Linux.deb

Minimalistisen RPM-paketin luomisen testaaminen

(Debian pohjaisissa linuxeissa joutuu asentamaan rpm-paketin, jotta pystymme tehdä RPM-paketin. Samalla kannattaa asentaa rpmlint-paketti (sudo apt install rpm rpm-common rpmlint))

Mene build-hakemistoon päätteessä: (cd build).

rm -r *
cmake ..
cpack -G RPM

Debian-paketin luominen riippuvuuksien kanssa

Aluksi luodaan projektille lisenssin sisältävän tiedoston (LICENSE) lisenssitieksti ja README.md-tiedosto. Ne tiedostot listään projektin juureen.

touch LICENSE
touch README.md

Lisäksi luomme uuden tiedoston: Packing.cmake, projektin cmake-alikansioon:

touch cmake/Packing.cmake

Tähän sisällöksi:

(Huom! kts. lähde: https://decovar.dev/blog/2021/09/23/cmake-cpack-package-deb-apt/#what-is-needed-to-enable-packing )

# -----------------------------------------------
# |                           OSUUS CPACK:IA VARTEN
# | Osuus kopioitu pitkälti:
# |  https://decovar.dev/blog/2021/09/23/cmake-cpack-package-deb-apt/
#
# Tarvittavat lisäykset Debian pakkausta ja normaalia pakkausta varten...
# Tähän on haettu/käännetty apuja https://karthikkalyanaraman.medium.com/creating-debian-packages-cmake-e519a0186e87
# ja varsinkin https://decovar.dev/blog/2021/09/23/cmake-cpack-package-deb-apt/
# Tästä on tiputettu pois oman apt-pakettilähteiden perustamisen osuus.

# Aluksi määritellään CPACK:lle vietävät muuttujat set-käskyillä.
set(CPACK_PACKAGE_NAME ${PROJECT_NAME}
    CACHE STRING "The resulting package name"
)

set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION}
    CACHE STRING "Package description for the package metadata"
)
set(CPACK_PACKAGE_VENDOR "Joku Yritys")

set(CPACK_VERBATIM_VARIABLES YES)

set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
SET(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_SOURCE_DIR}/_packages")

# https://unix.stackexchange.com/a/11552/254512
#set(CPACK_PACKAGING_INSTALL_PREFIX "/opt/some")#/${CMAKE_PROJECT_VERSION}")
# Tämä kommentoitiin, jotta paketti asennetaan 
# oikeisiin hakemistoihin eikä:
# /opt/some-alikansioon.

set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})

set(CPACK_PACKAGE_CONTACT "YOUR@E-MAIL.net")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "YOUR NAME")

set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")

# package name for deb. If set, then instead of 
# some-application-0.9.2-Linux.deb
# you'll get some-application_0.9.2_amd64.deb 
# (note the underscores too)
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
# that is if you want every group to have its own package,
# although the same will happen if this is not set 
# (so it defaults to ONE_PER_GROUP)
# and CPACK_DEB_COMPONENT_INSTALL is set to YES
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) #ONE_PER_GROUP)
# without this you won't be able to pack only specified component
set(CPACK_DEB_COMPONENT_INSTALL YES)

set( CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
    OWNER_READ OWNER_WRITE OWNER_EXECUTE
    GROUP_READ  GROUP_EXECUTE
    WORLD_READ WORLD_EXECUTE
)

#Määritellään debian-paketin riippuvuudet.
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.7-18), libboost-all-dev (>= 1.83)")
#Määritellään riippuvuudet automaattisesti !!!
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS YES)
#Määritellään rpm-paketin riippuvuudet.
#set(CPACK_RPM_PACKAGE_REQUIRES "libboost_date_time_legacy >= 1.65")
set(CPACK_RPM_PACKAGE_REQUIRES "libboost_date_time_legacy >= 1.65")

set(CPACK_RPM_PACKAGE_AUTOREQ YES)

Tässä siis kirjoitetiin cpack-ohjelmalle tarvitsemansa muuttujien arvot.

Lisää vielä include -rivi projektin juuren CMakeLists.txt-tiedoston loppupuolelle ennen include(CPack)-käskyä:

include ("cmake/Packing.cmake")

Tiedosto CMakeLists.txt-tiedosto näyttää tältä:

cmake_minimum_required(VERSION 3.21)

project(
    "eka-projekti"
    VERSION 0.1.0.2
    DESCRIPTION "Ensimmainen cmake-projektini"
    HOMEPAGE_URL "https://linux.fi"
    LANGUAGES "C" "CXX"
)

message("${PROJECT_NAME} on ensimmäinen projektimme, ja tässä on sen tiedot:")
message("Projektin versio on ${PROJECT_VERSION}")
message("Se koostuu neljästä osasta:")
message(${PROJECT_VERSION_MAJOR})
message(${PROJECT_VERSION_MINOR})
message(${PROJECT_VERSION_PATCH})
message(${PROJECT_VERSION_TWEAK})
message("Projektimme kuvaus on:${PROJECT_DESCRIPTION}")
message("Kotisivuksi olemme määritelleet tutun ${PROJECT_HOMEPAGE_URL}.")

find_package(boost_date_time REQUIRED)

configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.cmake ${CMAKE_SOURCE_DIR}/src/config.h)

add_library(mylib lib/mylib.cpp)

target_include_directories(mylib PUBLIC include)

add_executable("hello" "src/hello.cpp")

target_link_libraries(hello mylib boost_date_time)


# CPACK:n tarvitsemat käskyt
install(TARGETS hello )

# Deb-paketin ylläpitäjä eli paketin tekijä.
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Peran")
# Rpm-paketin myyjä. Laitoin samaksi, kuin Debian-paketin ylläpitäjä.
set(CPACK_RPM_PACKAGE_VENDOR ${CPACK_DEBIAN_PACKAGE_MAINTAINER})

set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

include("cmake/Packing.cmake")
include(CPack)





Käyttö

Katso myös