woodser / monero-cpp

C++ library for using Monero
https://woodser.github.io/monero-cpp/doxygen/annotated.html
MIT License
64 stars 28 forks source link

Error when linking a static library #27

Open ghost opened 1 year ago

ghost commented 1 year ago

I'm having some trouble linking the monero-cpp library to my simple project. I created a simple test project to help you figure out what exactly is going wrong. We are talking about MSYS2 MinGW64 assembly on Windows 10 x64. And so what needs to be done:

git clone git@github.com:sdcwen/monero-test.git
cd ./monero-test/
git submodule update --init
cd ./external/monero-cpp/
git checkout v0.7.6
cd ../../

Then you need to replace monero-test/external/monero-cpp/CMakeLists.txt with the monero-test/patch/CMakeLists.txt file. This file contains some fixes that allow you to add monero-cpp via add_subdirectory to your project.

-set(MONERO_PROJECT "${CMAKE_SOURCE_DIR}/external/monero-project" CACHE STRING "Monero project source directory")
+set(MONERO_PROJECT "${CMAKE_CURRENT_LIST_DIR}/external/monero-project" CACHE STRING "Monero project source directory")
...
-include_directories(src/)
+include_directories(${CMAKE_CURRENT_LIST_DIR}/src/)
...
-set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/external-libs)
+set(EXTERNAL_LIBS_DIR ${CMAKE_CURRENT_LIST_DIR}/external-libs)

With the help of ${CMAKE_CURRENT_LIST_DIR} I simply indicate that I want to build the rest of the paths relative to the current position of the file in the system. If this is not done, then add_subdirectory will not work correctly.

I also change the building of the dynamic library to a static one. I think this should not break anything, because all other libraries are imported from monero-project as static and the monero-cpp library itself can also be static.

-  add_library(monero-cpp SHARED ${LIBRARY_SRC_FILES})
+  add_library(monero-cpp STATIC ${LIBRARY_SRC_FILES})

Okay now we are ready to assemble. I use a customized QtCreator for building, which pulls the MSYS2 MinGW64 environment (which can be taken in monero-test/env/msys64/mingw64/paths.txt). Or you can do it through the MSYS2 MinGW64 terminal:

mkdir .build
cd ./.build
cmake -GNinja -DCMAKE_BUILD_TYPE=Debug ..
cmake --build . --target all

And now I'm getting an error that I can't do anything about and don't understand why it's happening at all:

[27/28] Linking CXX executable monero-test.exe
FAILED: monero-test.exe
cmd.exe /C "cd . && C:\msys64\mingw64\bin\c++.exe -g  @CMakeFiles\monero-test.rsp -o monero-test.exe -Wl,--out-implib,libmonero-test.dll.a -Wl,--major-image-version,0,--minor-image-version,0  && cd ."
C:/msys64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: D:/monero-test/external/monero-cpp/external/monero-project/build/release/src/device_tr
ezor/libdevice_trezor.a(protocol.cpp.obj):protocol.cpp:(.text+0x43cb): undefined reference to `hmac_keccak_hash'
collect2.exe: error: ld returned 1 exit status
[28/28] Linking CXX executable external\monero-cpp\scratchpad.exe
ninja: build stopped: subcommand failed.

In fact, I simply copied the contents of monero-test/external/monero-cpp/test/sample_code.cpp into my main.cpp file. And it is worth noting that sample_code.cpp itself compiles without problems. And also it is worth noting the problem with inclusions. In my main.cpp, I need to add the following includes at the very beginning:

#include <boost/asio.hpp>
#include <windows.h>

This is some kind of well-known problem. If this is not done, then we get a million errors. And what is again strange in sample_code.cpp there is no such problem. This issue is pulled from #include "wallet/monero_wallet_full.h".

Interestingly, if in monero-test/external/monero-cpp/CMakeLists.txt one or only one monero-cpp static lib is linked to the sample_code target, then sample_code compilation will end with the same error: undefined reference to hmac_keccak_hash.

In my opinion, this is very strange behavior. I can't explain it. In essence, the monero-cpp and sample_code targets contain the same set of static libraries in target_link_libraries, with only one difference, an additional unbound is linked to the monero-cpp target, which can also be removed and nothing bad will happen. That is, if in target_link_libraries for the monero-cpp target, register all the static libraries that are in the target_link_libraries for the sample_code target, and link the monero-cpp static library through target_link_libraries to the sample_code target, then this breaks compilation. If, instead, the entire list of static libraries is directly linked to the target sample_code (as is done now), then this does not break anything.

ghost commented 1 year ago

Maybe I'm using your project in a wrong way? Maybe I'm somehow doing something wrong with CMake scripts?

ghost commented 1 year ago

I would also like to draw attention to the main CMakeLists.txt. Why is just include_directories used everywhere? It would be more convenient for developers to use target_include_directories. If the include paths are connected to a certain target, then when I link this target to my target, I will inherit all the includes, all the paths to them. Otherwise, I have to propagate it myself in my CMakeLists.txt:

target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/src)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/contrib/epee/include)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/external)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/external/easylogging++)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/external/rapidjson/include)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/src)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/src/wallet)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/src/wallet/api)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/src/hardforks)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/monero-project/src/crypto/crypto_ops_builder/include)
target_include_directories(monero-cpp PUBLIC ${CMAKE_SOURCE_DIR}/external/monero-cpp/external/libsodium/include/sodium)

If this is not done, then with a simple use of the library, when I include #include "wallet/monero_wallet_full.h" somewhere in the depths of the includes, there is an include that cannot find its way. For example this happened to monero-test/external/monero-cpp/external/libsodium/include/sodium/export.h. It's just included somewhere as #include "export.h".

woodser commented 1 year ago

Have you confirmed that monero-cpp fully builds standalone? It only fails building from an external project?

ghost commented 1 year ago

Have you confirmed that monero-cpp fully builds standalone? It only fails building from an external project?

Yes, monero-cpp builds completely without problems if you follow the build instructions. The problems start when I try to link it as a static library into my project. Basically I'm doing the same as in sample_code, but it doesn't work.