I'm trying out the Conan package manager, and started writing a test C++ project that uses the Poco libraries. I produced a simple program that just decrypts a string using AES-256-CBC. I was extremely surprised to find the generated binary was almost 4 MB after building with Conan and Cmake. I tried tweaking the conanfile.txt and CmakeLists.txt files to only link the necessary libraries, but I either couldn't get the project to compile, or couldn't reduce the size of the compiled binary.
I'm pretty sure that PCRE, bzip2, SQLlite and more are getting linked in to my binary, as Poco depends on them. I'm just very confused as to why gcc isn't smart enough to figure out that the Poco code I'm calling is only using a small bit of OpenSSL code.
How can I only compile in/link what I need to, and keep my binary to a reasonable size?
conanfile.txt:
[requires]
poco/1.10.1
[generators]
cmake
CmakeLists.txt:
cmake_minimum_required(VERSION 3.7...3.18)
if(${CMAKE_VERSION} VERSION_LESS 3.12)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()
project(main)
add_definitions("-std=c++17")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
main.cpp:
#include <cstdlib>
#include <iostream>
#include <sstream>
#include "Poco/Base64Decoder.h"
#include "Poco/Crypto/Cipher.h"
#include "Poco/Crypto/CipherFactory.h"
#include "Poco/Crypto/CipherKey.h"
#include "Poco/DigestStream.h"
#include "Poco/SHA2Engine.h"
std::string sha512(std::string value);
std::string aesDecrypt(const std::string ciphertext, const std::string key, const std::string iv);
std::string getEnvVar(const std::string key);
std::string base64Decode(const std::string encoded);
int main(int argc, char** argv) {
std::string enc = "Ug7R5BQIosmn1yPeawSUIzY8N9wzASmI/w0Wz/xX7Yw=";
std::cout << aesDecrypt(enc, "admin", "7K/OkQIrl4rqUk8/1h+uuQ==") << "\n";
std::cout << sha512("Hello there") << "\n";
std::cout << getEnvVar("USER") << "\n";
return 0;
}
std::string aesDecrypt(const std::string ciphertext, const std::string key, const std::string iv) {
auto keyHash = sha512(key);
Poco::Crypto::Cipher::ByteVec keyBytes{keyHash.begin(), keyHash.end()};
auto rawIV = base64Decode(iv);
Poco::Crypto::Cipher::ByteVec ivBytes{rawIV.begin(), rawIV.end()};
auto &factory = Poco::Crypto::CipherFactory::defaultFactory();
auto pCipher = factory.createCipher(Poco::Crypto::CipherKey("aes-256-cbc", keyBytes, ivBytes));
return pCipher->decryptString(ciphertext, Poco::Crypto::Cipher::ENC_BASE64);
}
std::string sha512(const std::string value) {
Poco::SHA2Engine sha256(Poco::SHA2Engine::SHA_512);
Poco::DigestOutputStream ds(sha256);
ds << value;
ds.close();
return Poco::DigestEngine::digestToHex(sha256.digest());
}
std::string getEnvVar(const std::string key) {
char * val = getenv(key.c_str());
return val == NULL ? std::string("") : std::string(val);
}
std::string base64Decode(const std::string encoded) {
std::istringstream istr(encoded);
std::ostringstream ostr;
Poco::Base64Decoder b64in(istr);
copy(std::istreambuf_iterator<char>(b64in),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(ostr));
return ostr.str();
}
How I build the code:
#!/bin/bash
set -e
set -x
rm -rf build
mkdir build
pushd build
conan install ..
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build .
ls -lah bin/main
bin/main
There isn't anything wrong with what you are doing. It might just be that
pocohas a lot of dependencies and functionality. You can addmessage(STATUS "Linkd libraries: " ${CONAN_LIBS})in CMakeLists.txt and run cmake again to view the libraries that you are currently linking with when using${CONAN_LIBS}.You could also try using
conan_basic_setup(TARGETS)inCMakeLists.txtinstead of justconan_basic_setup(). If you do this, then you need to changetarget_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})totarget_link_libraries(${PROJECT_NAME} CONAN_PKG::poco). This allows you to have finer control of which libraries inconanfile.txtyou link with each target inCMaksLists.txt. But since yourconanfile.txtonly haspocoas a dependency, this shouldn't change anything.Another thing that you can try is checking if the
pocorecipe in conan has any options you can set to include/exclude parts of thepocolibrary. Run the command below (assumingpoco/1.10.1is already installed in your conan cache)This will show you the full recipe for
pocothat conan is using. Here I gotWhat you want to see is what the recipe has in
optionsanddefault_options. As far as I know, there is no way to query which options a recipe provides and what they do besides looking at the actual recipe source code like this.It seems that the poco recipe adds a lot of options from this
_poco_component_treedictionary. What you want to check is the options with namesenable_somethingwith valueTrue. Since these are added as options, it means that the client (you running conan) can control these when runningconan install. For instance, try the command below (you can add multiple-o poco:somethingto set multiple options)We can see in the
requirementsmethod in the recipe that only whenenable_data_sqliteisTrueconan will add "sqlite3/3.31.1" is a poco dependency. That means if you setenable_data_sqlitetoFalsethen it should not be included at all and your binary should get smaller.Since conan (and the poco developers or whoever created the recipe for poco) wants to make installing poco using conan as easy as possible it makes sense to include the most common parts of poco by default. Using conan options to disable parts of it is the way you can control this. You will have to try a few of these options to see what you get. In case you disable something you actually need you will get errors when compiling and/or linking your actual code.