matplotplusplus icon indicating copy to clipboard operation
matplotplusplus copied to clipboard

Undefined reference in matplot.lib with Visual Studio 2017

Open PJ127 opened this issue 4 years ago • 12 comments

Bug category

  • [x ] bug - compilation error
  • [ ] bug - compilation warning
  • [ ] bug - runtime error
  • [ ] bug - runtime warning
  • [ ] bug - logic error

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

  • [ ] cross-platform issue - linux
  • [ x] cross-platform issue - windows
  • [ ] cross-platform issue - macos

Environment Details:

  • OS: Windows 10
  • OS Version:
  • Compiler: Visual Studio 2017 win64
  • Compiler version:

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 avatar Sep 16 '20 09:09 PJ127

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()...

PJ127 avatar Sep 16 '20 09:09 PJ127

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?

alandefreitas avatar Sep 16 '20 15:09 alandefreitas

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.

lacc97 avatar Sep 16 '20 15:09 lacc97

@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.

alandefreitas avatar Sep 16 '20 19:09 alandefreitas

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.

PJ127 avatar Sep 17 '20 06:09 PJ127

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?

lacc97 avatar Sep 28 '20 21:09 lacc97

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

PJ127 avatar Sep 29 '20 06:09 PJ127

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.

alandefreitas avatar Oct 06 '20 00:10 alandefreitas

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.

codygray avatar May 30 '21 06:05 codygray

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

yu-ting-py avatar Feb 23 '23 05:02 yu-ting-py

@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.

codygray avatar Feb 23 '23 08:02 codygray

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?

yu-ting-py avatar Feb 23 '23 13:02 yu-ting-py