alandefreitas / matplotplusplus

Matplot++: A C++ Graphics Library for Data Visualization 📊🗾
https://alandefreitas.github.io/matplotplusplus/
MIT License
4.27k stars 331 forks source link

Undefined reference in matplot.lib with Visual Studio 2017 #61

Open PJ127 opened 4 years ago

PJ127 commented 4 years ago

Bug category

Describe the bug

int main(int argc, char **argv) {
    using namespace matplot;
    std::vector<double> x = linspace(0, 2 * pi);
    std::vector<double> y = transform(x, [](auto x) { return sin(x); });
    plot(x, y, "-o");
    show();
    return 0;
}

I am trying to compile this project (which works fine with MinGW) with Visual Studio 2017 and I get the following link 'unresolved symbol' errors:

80>matplot.lib(network.obj) : error LNK2019: symbole externe non résolu "class std::vector<struct nodesoup::Point2D,class std::allocator<struct nodesoup::Point2D> > __cdecl nodesoup::fruchterman_reingold(class std::vector<class std::vector<unsigned __int64,class std::allocator<unsigned __int64> >,class std::allocator<class std::vector<unsigned __int64,class std::allocator<unsigned __int64> > > > const &,unsigned int,unsigned int,unsigned int,double,class std::function<void __cdecl(class std::vector<struct nodesoup::Point2D,class std::allocator<struct nodesoup::Point2D> > const &,int)>)" (?fruchterman_reingold@nodesoup@@YA?AV?$vector@UPoint2D@nodesoup@@V?$allocator@UPoint2D@nodesoup@@@std@@@std@@AEBV?$vector@V?$vector@_KV?$allocator@_K@std@@@std@@V?$allocator@V?$vector@_KV?$allocator@_K@std@@@std@@@2@@3@IIINV?$function@$$A6AXAEBV?$vector@UPoint2D@nodesoup@@V?$allocator@UPoint2D@nodesoup@@@std@@@std@@H@Z@3@@Z) référencé dans la fonction "protected: void __cdecl matplot::network::process_force_layout(void)" (?process_force_layout@network@matplot@@IEAAXXZ)
80>matplot.lib(network.obj) : error LNK2019: symbole externe non résolu "class std::vector<struct nodesoup::Point2D,class std::allocator<struct nodesoup::Point2D> > __cdecl nodesoup::kamada_kawai(class std::vector<class std::vector<unsigned __int64,class std::allocator<unsigned __int64> >,class std::allocator<class std::vector<unsigned __int64,class std::allocator<unsigned __int64> > > > const &,unsigned int,unsigned int,double,double)" (?kamada_kawai@nodesoup@@YA?AV?$vector@UPoint2D@nodesoup@@V?$allocator@UPoint2D@nodesoup@@@std@@@std@@AEBV?$vector@V?$vector@_KV?$allocator@_K@std@@@std@@V?$allocator@V?$vector@_KV?$allocator@_K@std@@@std@@@2@@3@IINN@Z) référencé dans la fonction "protected: void __cdecl matplot::network::process_kawai_layout(void)" (?process_kawai_layout@network@matplot@@IEAAXXZ)
80>matplot.lib(network.obj) : error LNK2019: symbole externe non résolu "class std::vector<double,class std::allocator<double> > __cdecl nodesoup::size_radiuses(class std::vector<class std::vector<unsigned __int64,class std::allocator<unsigned __int64> >,class std::allocator<class std::vector<unsigned __int64,class std::allocator<unsigned __int64> > > > const &,double,double)" (?size_radiuses@nodesoup@@YA?AV?$vector@NV?$allocator@N@std@@@std@@AEBV?$vector@V?$vector@_KV?$allocator@_K@std@@@std@@V?$allocator@V?$vector@_KV?$allocator@_K@std@@@std@@@2@@3@NN@Z) référencé dans la fonction "protected: void __cdecl matplot::network::process_force_layout(void)" (?process_force_layout@network@matplot@@IEAAXXZ)
80>D:\Dev\compil\EyeDee-vs\bin\RelWithDebInfo\ETSimple.exe : fatal error LNK1120: 3 externes non résolus

Steps to Reproduce

find_library(my_matplot matplot HINTS ${possible_lib_paths} NO_DEFAULT_PATH) # correctly found
target_link_libraries(my_target PUBLIC my_matplot )

which lead to the above errors.

Alternatively, this does not work either during linking:

find_package(Matplot++) # correctly found
target_link_libraries(my_target PUBLIC matplot )

will complain during linking that it can't find matplot.lib, I am not sure why...:

80>LINK : fatal error LNK1104: impossible d'ouvrir le fichier 'matplot.lib' # visual output
x86_64-w64-mingw32/bin/ld.exe: cannot find -lmatplot # mingw output

Platform

Environment Details:

Additional context

When installing (shared_lib=ON), matplot.lib was copied to the install path, but not nodesoup.lib. Copying it from source\3rd_party\Release did not solve the problem.

PJ127 commented 4 years ago

I circumvented the problem, by adding nodesoup.lib to the install path, and adding it to the cmake links:

find_library(my_matplot matplot HINTS ${possible_lib_paths} NO_DEFAULT_PATH) # correctly found
target_link_libraries(my_target PUBLIC my_matplot )

find_library(my_nodesoup matplot HINTS ${possible_lib_paths} NO_DEFAULT_PATH) # correctly found
target_link_libraries(my_target PUBLIC my_nodesoup)

This compile and links well. Running the program display the graph properly (with GNUTERM=wxt). Thanks again, maybe there are some things to be done concerning the installation, and to find_package()...

alandefreitas commented 4 years ago

Hi @PJ127, thanks for the feedback. In what directory was the library installed?

@lacc97, maybe you understand this issue better than I do. I @PJ127 installed the library with shared_lib=ON. I guess matplot is using nodesoup as a private dependency and then when we export matplot only to Matplot++Targets, it installs something that has no way of accessing nodesoup. Is that right? Maybe we could force nodesoup to be static? Or just include it in Matplot++Targets?

lacc97 commented 4 years ago

It's odd. You shouldn't need nodesoup or cimg if you are using the shared lib.

Also, it should be

target_link_libraries(my_target PUBLIC Matplot++::matplot)

Maybe we could force nodesoup to be static?

Nodesoup is always built as a static lib and then it gets consumed when building the shared library version of matplot.

alandefreitas commented 4 years ago

@lacc97 That's true. Nodesoup is built with

add_library(nodesoup STATIC
      ${CMAKE_CURRENT_SOURCE_DIR}/nodesoup/src/algebra.cpp

It should always be static. Unless BUILD_SHARED_LIBS is overriding this option. But I don't think that makes sense at all.

I have no idea why @PJ127 would need to find_library(my_nodesoup) separately.

PJ127 commented 4 years ago

Thank you for your answers.

target_link_libraries(my_target PUBLIC Matplot++::matplot)

returns something like:

/wd4305 no such file or directory

I had to comment line 59 in lib\cmake\Matplot++\Matplot++Targets.cmake

 # INTERFACE_COMPILE_OPTIONS "/wd4305;/utf-8"

but it now returns:

undefined reference to `matplot::gca()'
...

as if it did not find the matplot library. Strange, maybe I did something wrong.

lacc97 commented 4 years ago

It's odd. If CMake can't find the required library file (the actual .lib file) it's supposed to complain about it at configure time.

Can you maybe try again with a clean build directory?

PJ127 commented 4 years ago

Okay, I'll try when I have my Windows machine back in a few days.

alandefreitas commented 4 years ago

I included an integration test/example in test/integration.

I also extended the build workflow to always test this build script: https://github.com/alandefreitas/matplotplusplus/blob/903d1aa7883e94040c0ed3a64a415030c262f082/.github/workflows/build.yml#L87

@lacc97, I hope that's helpful somehow.

Although that's not the problem @PJ127 is having, it seems like matplot++-config.cmake is not going to the default directory on windows, though: https://github.com/alandefreitas/matplotplusplus/runs/1212059007?check_suite_focus=true#step:7:59 https://github.com/alandefreitas/matplotplusplus/runs/1212059007?check_suite_focus=true#step:8:32

I don't know if this should really be going to another directory by default. CMake recommends C:/Program Files (x86)/ for installing but it doesn't look for matplot++-config.cmake there.

codygray commented 3 years ago

I had this same issue when I downloaded the pre-built binary package (https://github.com/alandefreitas/matplotplusplus/releases), installed it, and tried to build with Visual Studio 2019. In the binary distribution after installation, the matplot.lib object library is in the main lib directory, but the nodesoup.lib object library is in the Matplot++ subdirectory. To work around these linker errors, I had to:

  1. Add nodesoup.lib as an explicit dependency (either with #pragma comment(lib, "nodesoup.lib") in the source or by adding nodesoup.lib to the "Linker → Input → Additional Dependencies" in the Visual Studio project properties).

  2. Add the lib/Matplot++ subdirectory to the linker's search path (Linker → General → Additional Library Directories).

Clearly, the way that the matplot.lib object library is being built on Windows is not merging the contents of nodesoup.lib into it, nor does it even include a reference to it in a relative path (i.e., Matplot++/nodesoup.lib). That is fairly normal; adding a "reference" from one project to another in Visual Studio only sets up a build dependency, it doesn't perform any sort of linking.

You can ensure that the two object libraries are merged into a single one by running a command like the following as a post-build event:

lib.exe /out:matplot_complete.lib matplot.lib nodesoup.lib

(If you're more familiar with the Gnu/*nix toolchain, this would be the moral equivalent of ar.)

Alternatively, starting with Visual Studio 2015 Update 2, you can use the /wholearchive switch when building: https://docs.microsoft.com/en-us/cpp/build/reference/wholearchive-include-all-library-object-files

I'm not sure if there's a better way of doing this with CMake; I haven't had the pleasure of using that tool yet.

yu-ting-py commented 1 year ago

Hi

I have a similar issue as I run a script (line.cpp) on my ubuntu20 machine with matplot++ installed.

#include <cmath>
#include <matplot/matplot.h>

int main() {

    using namespace matplot;
    std::vector<double> x = linspace(0, 2 * pi);
    std::vector<double> y1 = transform(x, [](auto x) { return sin(x); });
    std::vector<double> y2 = transform(x, [](auto x) { return sin(x - 0.25); });
    std::vector<double> y3 = transform(x, [](auto x) { return sin(x - 0.5); });
    plot(x, y1, "g", x, y2, "b--o", x, y3, "c*");

    show();
    return 0;
}

with command:

g++ -std=c++17 line.cpp -o line.o

Eventually, it shows

/usr/bin/ld: /tmp/ccDNNI2Q.o: in function `main':
line.cpp:(.text+0x34): undefined reference to `matplot::linspace(double, double)'
collect2: error: ld returned 1 exit status

Do you have a solution to that?

Thanks in advance

codygray commented 1 year ago

@yu-ting-py That is a different problem than what is described here, with a much simpler solution. You need to link to the MatPlot++ library. Pass the option -l:matplot.lib when you run the g++ command. (You may also need to specify the path to where the linker can find the .lib file with the -L option.)

You may struggle to get all the dependencies linked in unless you have a thorough understanding of the C++ build toolchain. The instructions in the repository assume you're using CMake to build MatPlot++, which is what I would recommend doing unless you have a specific reason not to use it.

yu-ting-py commented 1 year ago

I have installed MatPlot++ with CMake according to the readme file. Do I need to use CMake instead of g++ command every time I plot something?