Krótki tutorial menadżera pakietów Conan
Tworzenie aplikacji to zazwyczaj świetna przygoda. Możemy modelować zachowania aplikacji, próbując wynaleźć coś, czego jeszcze nie ma. Jednym z problemów związanych z szybkim dostarczaniem swoich pomysłów jest niewątpliwie infrastruktura, na której się opieramy. Tworząc swoją grę, nie chcemy tworzyć silnika od nowa - lepiej skorzystać z istniejącego już rozwiązania. Kiedy myślimy o komunikacji klient-serwer, raczej nie mamy na myśli tworzenia kodu obsługi socketów, a - modelowanie komunikatów, które będą wysyłane przez kod, od którego nasza aplikacja będzie zależeć, czyli zależność. Zapraszam na wpis, w którym przedstawię podstawy pracy z menadżerem zależności Conan.
O zależnościach słów kilka
Zależnością nazywamy (nie zawsze) obcy nam kod, który chcemy wykorzystać podczas tworzenia aplikacji. Bardzo często naszymi zależnościami jest infrastruktura, czyli kod pozawalający nam np. na komunikację ze światem zewnętrznym (jak biblioteki z rodziny Poco), biblioteki użytkowe, czyli takie, z których możemy korzystać niezależnie od tego, czym zajmuje się nasza aplikacja (doskonałym przykładem jest tutaj biblioteka boost) oraz frameworki, które stają się niejako formą nałożoną na naszą logikę biznesową. Jako zależność traktować możemy zarówno cudzy kod, jak i skompilowane przez autora pliki binarne. Jest to dosłownie wszystko, co jest nam potrzebne, abyśmy mogli bardziej pochylić się nad tym, co ma robić nasza aplikacja, niż jak ma to robić.
Zależności po staremu
Dawniej, jak chcieliśmy skorzystać z cudzego kodu, mieliśmy kilka możliwości. Pierwszym ze sposobów na dorzucanie obcych rozwiązań było załączenie do repozytorium cudzego kodu i dopisanie w systemie budowania kilku formułek, dzięki którym będziemy widzieli w projekcie obce symbole. Wad tego rozwiązania jest kilka:
- już sama świadomość obcego kodu w naszym repozytorium staje się wielkim minusem
- aktualizacja biblioteki odbywa się przez kopiuj/wklej wielu plików źródłowych, co nie należy wcale do wygodnych rozwiązań
- łatwo jest pokusić się o modyfikację cudzej biblioteki, przez co tracimy możliwość jej aktualizacji, kiedy pojawi się nowa wersja jej kodu.
Drugą formą załączenia zależności po staremu było wykorzystanie skompilowanych już wcześniej plików binarnych (biblioteki statyczne lub dynamiczne). Oznacza to, że do implementacji biblioteki zewnętrznej nie mamy już dostępu bezpośrednio. Nie jest to ciągle czyste rozwiązanie, ponieważ:
- nagłówki tej biblioteki ciągle zostają w naszym repozytorium, jak również plik ze skompilowaną biblioteką
- aktualizacja tej paczki nieco się komplikuje: musimy dbać zarówno synchronizację w wersji pomiędzy jej plikami nagłówkowymi oraz plikiem binarnym
- nieco trudniej jest pracować na różnych systemach operacyjnych, ze względu na to, że musielibyśmy załączyć kilka plików z tą biblioteką, skompilowanych pod inne systemy operacyjne (co wpływa negatywnie na aktualizację jej wersji)
Trzecim sposobem na radzenie sobie z zależnościami jest korzystanie z bibliotek zainstalowanych na systemie operacyjnym, na którym pracujemy. Tutaj wad jest nieco więcej:
- instalacja nowego środowiska wydłuża się wraz z długością listy zależności, gdyż wszystko musimy instalować sami
- decyzja o aktualizacji wersji biblioteki wymaga zgody i pracy całego zespołu
- aktualizacja wersji biblioteki może wymagać od nas pracy przy wszystkich projektach, nad którymi obecnie pracujemy
- dosyć łatwo jest zgubić informację na temat wersji aktualnie zewnętrznych bibliotek
- niewygodną (jeżeli w ogóle możliwą) staje się praca nad dwoma projektami, które korzystają z dwóch różnych wersji tej samej biblioteki
Zależności dziś
Dynamiczny rozwój wielu różnych technologii (niekoniecznie związanych z C++) spowodował, że ogólnie pojęta inżynieria oprogramowania stała się bardziej dojrzała. W dzisiejszym świecie już wiemy, czego i w jaki sposób potrzebujemy używać.
Przede wszystkim, ważna dla nas jest zcentralizowana informacja o tym, z jakich zależności chcemy korzystać oraz w jakich wersjach. Kiedy posiadamy taką informację, możemy wziąć się za ich ściągnięcie i skonfigurowanie naszego projektu tak, abyśmy mógli z nich korzystać. Gdybyśmy robili to “po staremu”, to robilibyśmy to manualnie. Dzisiaj jednak wiemy, że tą operację można bardzo łatwo zautomatyzować. Zlecamy to zadanie menadżerowi zależności, który ściąga wszystkie potrzebne nam zależności i kompiluje je.
Jedyne, co do nas należy, to określenie listy wymaganych przez nas zależności oraz odpowiednie skonfigurowanie przed pierwszym ich użyciem.
Kiedy przesadzimy z zależnościami…
Zanim przejdziemy do tutoriala, warto wspomnieć o jednej kwestii, tak ku przestrodze. Menadżer zależności to świetne narzędzie, które rozwiązuje nam wiele problemów. Moje doświadczenie zawodowe w innej technologii spowodowało jednak, że podchodzę do tego tematu nieco bardziej ostrożnie.
Wiedzmy, że każda zależność, każdy kawałek kodu dorzucony z zewnątrz może posiadać swoje własne zależności, na które już możemy zupełnie nie zwrócić uwagi. Myśląc o ładnym sposobie rozwiązania problemu potrafimy często zapominać o tym, że może ono nie działać optymalnie. Czasami w ogóle nie zastanawiamy się, jak coś działa. Ważne, że można to ładnie wpiąć w naszą aplikację. Przez takie podejście uzależniamy się często od powolnego, czasami nawet mało bezpiecznego kodu. Myślimy, że jesteśmy bezpieczni, bo piszemy bezpieczny kod. Nie zwracamy natomiast uwagi na pozostały kod, którego nie napisaliśmy, a który jednak również jest wykonywany przez naszą aplikację.
Ja sam zawsze staram się przejrzeć wcześniej pobrany kod tak, by móc ocenić, czy to rozwiązanie aby na pewno mi odpowiada. Polecam, abyście zawsze starali się jak najwięcej dowiedzieć o kodzie, od którego zależycie i świadomie wybrać to, czy chcemy z niego korzystać, czy nie. Czasami własna prosta implementacja jest o wiele bardziej warta niż obca kobyła.
Instalacja dwóch wersji tej samej biblioteki
Świadomość o tym, że obcy kod może posiadać własne zależności powoduje istnienie problemu, który chyba jest niezależny od technologii, w obszarze której działamy: mowa o zjawisku nazywanym dependency hell
.
Przykład 1
Wyobraźmy sobie sytuację, w której korzystamy z trzech bibliotek: X w wersji 1.0
, Y w wersji 1.0
oraz Z w wersji 2.0
. Instalujemy wszystkie zależności, próbujemy skompilować nasz kod i naszym oczom ukazuje się błąd. Okazuje się, że biblioteka Y
wymaga do działania biblioteki Z
w wersji 1.0
. Nie możemy spełnić tego wymagania, ponieważ nasza aplikacja korzysta z biblioteki Z
w wersji 2.0
. Biblioteka Y
może pomimo wszystko chcieć załączyć Z
w wersji 1.0
do naszego projektu, ale ze względu na zduplikowane symbole biblioteki Z
nasz kod nie skompiluje się. Mamy więc do wyboru dwie opcje: zrezygnować z biblioteki Y
lub pracować na bibliotece Z
w wersji 1.0
.
Przykład 2
Załóżmy, że korzystamy z tego samego zestawu bibliotek, co wyżej: X w wersji 1.0
, Y w wersji 1.0
oraz Z w wersji 2.0
. Niech biblioteka X
zależy od biblioteki V
w wersji 1.0
, a biblioteka Y
zależy od biblioteki V
w wersji 2.0
. Podobnie, jak w przykładzie pierwszym, mamy do czynienia z konfliktem zależności. W tym jednak przypadku będzie nam nieco trudniej go rozwiązać: będziemy musieli tak dobrać wersje bibliotek X
oraz Y
, aby korzystały z tej samej wersji biblioteki V
. Może którakolwiek wersja X
albo Y
w ogóle nie korzysta z V
? Możliwy jest również scenariusz mówiący, że nie uda nam się rozwiązać pokojowo tego konfliktu i będziemy musieli wyrzucić z naszego projektu X
albo Y
.
Może się powtórzę, ale warto jest wysilić się nieco bardziej i poznać bliżej zależności z którymi pracujemy na codzień. Kiedy wejdzie nam to w krew, będziemy mądrzej decydowali o tym, jaki kod zewnętrzny chcemy załączyć do naszego projektu.
Przedstawiam Wam projekt Conan
Projekt Conan jest otwartoźródłowym, rozproszonym systemem rozwiązywania zależności o architekturze klient-serwer. Aby móc korzystać z jego dobrodziejstw, należy najpierw go pobrać i zainstalować. Do wyboru mamy kilka opcji:
- na Windowsie możemy go zainstalować przez instalator
- na Macu za pomocą menadżera pakietów brew
- na Ubuntu i Debiana możemy ściągnąć pakiet *.deb
- na ArchLinux możemy skorzystać z menadżera pakietów
yay
- na pozostałych systemach operacyjnych możemy zainstalować Conana przez skrypt instalacyjny napisany w Pythonie, bądź samemu ściągnąć kod źródłowy i skompilować go.
Kiedy już zainstalujemy Conana w naszym systemie, będziemy mogli korzystać z niego w terminalu.
Przeszukiwanie repozytorium
Pierwszą rzeczą, którą należy zrobić jest niewątpliwie uświadomienie sobie, z jakiej zależności/biblioteki chcemy korzystać w naszej aplikacji. Na potrzeby tego wpisu załóżmy, że chcemy stworzyć aplikację, która zwróci nam kod Base64 pliku, którego ścieżkę przekażemy jako parametr wywołania. O ile logikę czytania z pliku czy obsługi parametrów napiszemy sami, to nie chcemy już pisać algorytmu kodowania Base64. Możemy skorzystaż z klasy Poco::Base64Encoder
, której dokumentację możemy znaleźć tutaj.
Wiemy już, że do naszego projektu chcemy załączyć bibliotekę Poco. Możemy próbować znaleźć pakiet z tą biblioteką, korzystając z polecenia:
conan search Poco* --remote=conan-center
Uwaga. Gdybyśmy nie podali parametru --remote
, Conan szukałby zainstalowanych już pakietów w naszym systemie operacyjnym. My jednak chcemy znaleźć pakiet w głównym repozytorium. U mnie polecenie to zwróciło odpowiedź:
Existing package recipes:
Poco/1.7.8p3@pocoproject/stable
Poco/1.7.9p1@pocoproject/stable
Poco/1.7.9p2@pocoproject/stable
Poco/1.7.9@pocoproject/stable
Poco/1.8.0@pocoproject/stable
Poco/1.8.0.1@pocoproject/stable
Poco/1.8.1@pocoproject/stable
Poco/1.9.0@pocoproject/stable
Poco/1.9.1@pocoproject/stable
Poco/1.9.2@pocoproject/stable
Poco/1.9.3@pocoproject/stable
Poco/1.9.4@pocoproject/stable
Wspaniale. Widać, że jest kilka wersji, z których możemy skorzystać. Spróbujmy w takim razie przeglądnąć pakiet z wersją 1.9.4
, ponieważ domyślnie chcielibyśmy korzystać z najnowszych wersji bibliotek. Aby podglądnąć więcej szczegółów na temat tej wersji, należy wpisać polecenie:
conan inspect Poco/1.9.4@pocoproject/stable --remote=conan-center
Wzamian dostaniemy dosyć rozległą paczkę informacji:
name: Poco
version: 1.9.4
url: http://github.com/pocoproject/conan-poco
homepage: None
license: The Boost Software License 1.0
author: None
description: Modern, powerful open source C++ class libraries for building network- and internet-based applications that run on desktop, server, mobile and embedded systems.
topics: None
generators: ('cmake', 'txt')
exports: None
exports_sources: ('CMakeLists.txt', 'PocoMacros.cmake')
short_paths: False
apply_env: True
build_policy: None
revision_mode: hash
settings: ('os', 'arch', 'compiler', 'build_type')
options:
cxx_14: [True, False]
enable_apacheconnector: [True, False]
enable_cppparser: [True, False]
enable_crypto: [True, False]
enable_data: [True, False]
enable_data_mysql: [True, False]
enable_data_odbc: [True, False]
enable_data_sqlite: [True, False]
enable_json: [True, False]
enable_mongodb: [True, False]
enable_net: [True, False]
enable_netssl: [True, False]
enable_netssl_win: [True, False]
enable_pagecompiler: [True, False]
enable_pagecompiler_file2page: [True, False]
enable_pdf: [True, False]
enable_pocodoc: [True, False]
enable_redis: [True, False]
enable_sevenzip: [True, False]
enable_tests: [True, False]
enable_util: [True, False]
enable_xml: [True, False]
enable_zip: [True, False]
fPIC: [True, False]
force_openssl: [True, False]
poco_unbundled: [True, False]
shared: [True, False]
default_options:
cxx_14: False
enable_apacheconnector: False
enable_cppparser: False
enable_crypto: True
enable_data: True
enable_data_mysql: False
enable_data_odbc: False
enable_data_sqlite: True
enable_json: True
enable_mongodb: True
enable_net: True
enable_netssl: True
enable_netssl_win: True
enable_pagecompiler: False
enable_pagecompiler_file2page: False
enable_pdf: False
enable_pocodoc: False
enable_redis: True
enable_sevenzip: False
enable_tests: False
enable_util: True
enable_xml: True
enable_zip: True
fPIC: True
force_openssl: True
poco_unbundled: False
shared: False
Z powyższych informacji wynika między innymi, że możemy skorzystać z generatora CMake, co jest dobrą informacją. Dodatkowo mamy tutaj informację na temat flag, które możemy przekazać, sterując procesem budowania plików binarnych tej biblioteki. Są to bardzo przydatne narzędzia dla zaawansowanych użytkowników. My jednak skorzystamy z domyślnej konfiguracji, która dostarczy nam wersję skompilowaną do plików bibliotek statycznych (na systemie MacOS są to pliki z rozszerzeniem *.a
). Na potrzeby tego tutoriala powinno nam to wystarczyć.
Brakuje tutaj jednak jeszcze jednej, ważnej informacji: od jakich bibliotek zależy Poco? Czy podczas instalacji Poco zostanie do naszego projektu dołączone coś, o czym możemy nie mieć pojęcia? Istnieje odpowiedź na te pytania, jednakże musimy jej szukać gdzie indziej. Aby wyświetlić listę zależności, które zostaną załączone do naszego projektu oprócz Poco, należy skorzystać z polecenia conan info
:
conan info Poco/1.9.4@pocoproject/stable --remote=conan-center
Nasz menadżer pakietów zwróci nam odpowiedż w stylu:
OpenSSL/1.0.2o@conan/stable
ID: 75c89fa3e1091e5c3911f9847d6d4abd7c60e60f
BuildID: None
Remote: conan-center=https://conan.bintray.com
URL: http://github.com/lasote/conan-openssl
License: The current OpenSSL licence is an 'Apache style' license: https://www.openssl.org/source/license.html
Recipe: Cache
Binary: Cache
Binary remote: conan-center
Creation date: 2019-03-05 11:54:02
Required by:
Poco/1.9.4@pocoproject/stable
Requires:
zlib/1.2.11@conan/stable
Poco/1.9.4@pocoproject/stable
ID: 6ccb5ed40499b5b1e6ba8aa095c7d47b5a9a76ff
BuildID: None
Remote: conan-center=https://conan.bintray.com
URL: http://github.com/pocoproject/conan-poco
License: The Boost Software License 1.0
Recipe: Cache
Binary: Download
Binary remote: conan-center
Creation date: 2019-09-18 10:14:15
Requires:
OpenSSL/1.0.2o@conan/stable
zlib/1.2.11@conan/stable
ID: 534dcc368c999e07e81f146b3466b8f656ef1f55
BuildID: None
Remote: conan-center=https://conan.bintray.com
URL: http://github.com/conan-community/conan-zlib
Homepage: https://zlib.net
License: Zlib
Author: Conan Community
Recipe: Cache
Binary: Cache
Binary remote: conan-center
Creation date: 2019-06-21 10:50:37
Required by:
OpenSSL/1.0.2o@conan/stable
Jak można przypuszczać, w tej odpowiedzi zawarte są informacje, które bardziej nas będą interesować. Okazuje się, że wraz z naszą biblioteką zostaną zainstalowane dwie inne paczki! Biorąc pod uwagę moje doświadczenie w innej technologii powiem: dobrze, że tylko dwie. Mogło być stanowczo gorzej :) Niemniej jednak, projekt Poco opiera się na bibliotece OpenSSL/1.0.2o@conan/stable
, a ta z kolei do swojego działania wymaga obecności zlib/1.2.11@conan/stable
. Warto jest rozważyć w takim razie, czy aby na pewno chcemy zainstalować i korzystać z Poco. Jeżeli dobrze kojarzę, to do kodowania Base64
nie jest potrzeby OpenSSL, zatem nasza binarka będzie zawierała sporą ilość symboli, z których nie będzie korzystała. Warto jest rozważyć, czy nie lepiej jest napisać własną implementację kodowania Base64, albo chociaż próbować znaleźć inną, mniejszą paczkę, która umożliwia to samo. Na potrzeby tego wpisu mimo wszystko skorzystamy z Poco, ponieważ jego celem nie jest super optymalne wykorzystanie kodowania Base64
.
Instalacja pakietu
Kiedy już zdecydowaliśmy się na korzystanie z Poco w naszej aplikacji, przychodzi krok na jego instalację. Instalować będziemy zgodnie z przyjętymi wcześniej zasadami:
- budujemy projekt używając CMake. Wszystkich, którzy nie znają zasady działania CMake zapraszam do moich wpisów, w których wyjaśniam jego zasadę działania: I nastało nowe - CMake teoretycznie oraz CMake w praktyce.
- informacje o zależnościach wewnątrz naszego projektu zostawiamy w repozytorium
Pierwszym krokiem będzie utworzenie pliku conanfile.txt
, w którym poinformujemy Conana, z czego chcemy korzystać:
[requires]
Poco/1.9.4@pocoproject/stable
[generators]
cmake
Kto zna angielski, ten wie, że wymagamy biblioteki Poco w wersji 1.9.4
, pracując z CMake.
Drugim krokiem będzie konfiguracja naszego projektu po stronie CMake:
cmake_minimum_required(VERSION 3.10)
project(MyProject)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(execFile main.cpp)
target_link_libraries(execFile ${CONAN_LIBS})
Pierwszą niestandardową rzeczą, którą widzimy jest załączenie pliku ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake
. Plik ten jest zestawem informacji, które są potrzebne do zlokalizowania przez nas wszystkich potrzebnych bibliotek i zostanie wygenerowany przez nas w następnym kroku. Plik ten zawiera w sobie również definicję funkcji conan_basic_setup
, która zostaje wywołana zaraz po jego załadowaniu. Funkcja ta inicjalizuje całą logikę załączania bibliotek. Ostatnią rzeczą, jaką niestandardowo potrzebujemy zrobić, to załączyć biblioteki do naszego pliku binarnego. Warto wspomnieć, że ładujemy tutaj wszystkie biblioteki. Nie jest to zalecane, kiedy pracujemy nad dużym projektem z wieloma targetami. Powinniśmy sami rozważyć, które biblioteki będą potrzebne w której części projektu. Na przykład, nie potrzebujemy załączać do naszego pliku wykonywalnego bibliotek OpenSSL oraz Zlib, ponieważ algorytm kodowania Base64
nie potrzebuje symboli z tych bibliotek podczas swojej pracy.
Trzecim etapem jest instalacja bibliotek. Do tego celu tworzymy katalog build
w głównym katalogu naszego projektu. Po jego utworzeniu wchodzimy do niego i z wewnątrz wykonujemy polecenie:
conan install ..
Katalog ..
wskazuje ścieżkę, pod którą Conan znajdzie plik z listą zależności, których potrzebujemy do działania programu. W naszym wypadku jest to plik conanfile.txt
. Podczas ściągania paczek na wyjście zostają zwrócone logi tak, byśmy mogli widzieć, w którym momencie pracy Conana obecnie się znajdujemy. U mnie logi wyglądały tak:
Configuration:
[settings]
arch=x86_64
arch_build=x86_64
build_type=Release
compiler=apple-clang
compiler.libcxx=libc++
compiler.version=10.0
os=Macos
os_build=Macos
[options]
[build_requires]
[env]
conanfile.txt: Installing package
Requirements
OpenSSL/1.0.2o@conan/stable from 'conan-center' - Cache
Poco/1.9.4@pocoproject/stable from 'conan-center' - Cache
zlib/1.2.11@conan/stable from 'conan-center' - Cache
Packages
OpenSSL/1.0.2o@conan/stable:75c89fa3e1091e5c3911f9847d6d4abd7c60e60f - Cache
Poco/1.9.4@pocoproject/stable:6ccb5ed40499b5b1e6ba8aa095c7d47b5a9a76ff - Download
zlib/1.2.11@conan/stable:534dcc368c999e07e81f146b3466b8f656ef1f55 - Cache
zlib/1.2.11@conan/stable: Already installed!
OpenSSL/1.0.2o@conan/stable: Already installed!
Poco/1.9.4@pocoproject/stable: Retrieving package 6ccb5ed40499b5b1e6ba8aa095c7d47b5a9a76ff from remote 'conan-center'
Downloading conanmanifest.txt: 100%|##########| 50.2k/50.2k [00:00<00:00, 1.84MB/s]
Downloading conaninfo.txt: 100%|##########| 2.37k/2.37k [00:00<00:00, 2.70MB/s]
Downloading conan_package.tgz: 100%|##########| 5.06M/5.06M [00:01<00:00, 3.10MB/s]
Decompressing conan_package.tgz: 100%|##########| 5.06M/5.06M [00:00<00:00, 9.64MB/s]
Poco/1.9.4@pocoproject/stable: Package installed 6ccb5ed40499b5b1e6ba8aa095c7d47b5a9a76ff
Poco/1.9.4@pocoproject/stable: Downloaded package revision 0
conanfile.txt: Generator cmake created conanbuildinfo.cmake
conanfile.txt: Generator txt created conanbuildinfo.txt
conanfile.txt: Generated conaninfo.txt
conanfile.txt: Generated graphinfo
Powyższy proces wygenerował nam plik conanbuildinfo.cmake
, który załączyliśmy w pliku CMakeLists.txt
wcześniej.
Właśnie przeszliśmy przez proces instalacji. Teraz skupimy się na procesie korzystania z Poco wewnątrz naszego projektu.
Korzystanie z zainstalowanej biblioteki
W linijce siódmej pliku CMakeLists.txt
zamieściliśmy informację o pliku źródłowym naszego programu. Utwórzmy w takim razie w głównym katalogu projektu plik main.cpp
oraz zaimplementujmy zachowanie naszego programu.
#include <iostream>
#include <fstream>
#include <sstream>
#include <Poco/Base64Encoder.h>
std::string readFile(const std::string &path) {
std::ifstream input(path);
if (!input.good()) {
throw std::runtime_error(path + ": File doesn't exist");
}
return std::string((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
}
std::string base64Encode(const std::string &input) {
std::stringstream stream;
Poco::Base64Encoder encoder(stream);
encoder << input;
encoder.close();
return stream.str();
}
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "No input file specified";
return -1;
}
try {
const std::string fileContent = readFile(argv[1]);
const std::string base64Output = base64Encode(fileContent);
std::cout << base64Output << std::endl;
return 0;
} catch (std::runtime_error &e) {
std::cerr << e.what();
return -2;
}
}
Powyższy program składa się z kilku części. Pierwsza z nich waliduje naszą listę parametrów. Druga wczytuje zawartość pliku podanego w pierwszym parametrze (jeżeli plik nie istnieje, to wyrzucany jest wyjątek). Trzecia część korzysta z dobrodziejstwa biblioteki Poco, konwertując treść naszego pliku na zakodowany ciąg.
Jak możemy zauważyć, korzystamy z biblioteki w sposób taki jak w dokumentacji: ładujemy odpowiedni plik nagłówkowy oraz pracujemy na obiekcie odpowiedniej klasy. Można by rzec, że jedyne co tutaj jest nam potrzebne, to znajomość samej biblioteki.
W jaki sposób skompilujemy naszą aplikację? Tutaj nie ma żadnej magii! :) Wystarczy przejść do katalogu, w którym zawiera się cały cache CMake (u nas to katalog build
), zbudować projekt i skompilować. Polecenia wyglądają następująco:
cd build
cmake build ..
make
Teraz możemy używać naszego programu, którego plik wykonywalny execFile
powinien znajdować się w katalogu build/bin
.
Zachęcam Was do przeglądnięcia tego małego projektu na GitHubie.
Podsumowanie
Dzisiejszy wpis wprowadził nas delikatnie w temat zależności od kodu z zewnątrz. Dowiedzieliśmy się, dlaczego warto jest stosować system rozwiązywania zależności oraz jakie niebezpieczeństwa może za sobą nieść zbytnie ufanie tego typu oprogramowaniu. Poznaliśmy również problem związany z konfliktami pomiędzy zależnościami (dependency hell). Następnie przeszliśmy do krótkiego tutoriala do narzędzia Conan na przykładzie projektu wykorzystującego fragment biblioteki Poco.
Przestroga
Ponownie powtórzę się, ale uważam, że warto: Pamiętajmy, by zawsze poznać nieco bardziej pakiet, który chcemy zaciągnąć i sprawdzić jego zależności. Instalacja zewnętrznych zależności jest bardzo wygodna, ale przez zbyt wielkie poczucie wygody możemy stracić na bezpieczeństwie aplikacji.