Czy wiesz, że jesteśmy również na Slacku? Dołącz do nas już teraz klikając tutaj!

Krótki tutorial menadżera pakietów Conan


2019-10-24, 01:09

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.



Marcin Kukliński

Zawodowo backend developer, hobbystycznie pasjonat języka C++. Po godzinach poszerza swoją wiedzę na takie tematy jak teorii kompilacji oraz budowa formatów plików. Jego marzeniem jest stworzyć swój własny język programowania.

Pssst! Używamy Cookies. Poprzez używanie naszego serwisu zgadzasz się na odczytywanie i zapisywanie Cookies w swojej przeglądarce.
Polityka Prywatności