celtera / libremidi

A modern C++ MIDI 1 / MIDI 2 real-time & file I/O library. Supports Windows, macOS, Linux and WebMIDI.
Other
463 stars 51 forks source link

usage on windows? #110

Closed cole-io closed 5 months ago

cole-io commented 5 months ago

CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)

set (CMAKE_CXX_STANDARD 20)

project(libremidi_project CXX)

find_library(LIBREMIDI_LIB libremidi)
add_executable(test test.cpp)

target_link_libraries(test PRIVATE "${LIBREMIDI_LIB}")

test.cpp:

#include <string>
#include <iostream>

#include <libremidi/libremidi.hpp>

int main(int, char**) {
        libremidi::observer obs;
        for(const libremidi::input_port& port : obs.get_input_ports())
                std::cout << port.port_name << '\n';
}

Everything goes fine until linking. C:/bridge/lib is a valid path that contains an MSVC-compiled libremidi.lib (see -D flag below)

PS C:\bridge\libremidi_test\build> cmake -G "Ninja" -DCMAKE_PREFIX_PATH=C:/bridge/lib ..
-- The CXX compiler identification is MSVC 19.39.33523.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.39.33519/bin/Hostx64/x64/cl.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.7s)
-- Generating done (0.0s)
-- Build files have been written to: C:/bridge/libremidi_test/build

Attempting to link the executable fails with a bunch of undefined symbols inside libremidi.lib:

PS C:\bridge\libremidi_test\build> cmake --build .
[1/2] Building CXX object CMakeFiles\test.dir\test.cpp.obj
J:\usr\local\include\libremidi/error.hpp(4): warning C4068: unknown pragma 'GCC'
J:\usr\local\include\libremidi/error.hpp(5): warning C4068: unknown pragma 'GCC'
J:\usr\local\include\libremidi/error.hpp(6): warning C4068: unknown pragma 'GCC'
J:\usr\local\include\libremidi/error.hpp(8): warning C4068: unknown pragma 'GCC'
[2/2] Linking CXX executable test.exe
FAILED: test.exe
C:\WINDOWS\system32\cmd.exe /C "cd . && "C:\Program Files\CMake\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\test.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\mt.exe --manifests  -- C:\PROGRA~1\MIB055~1\2022\COMMUN~1\VC\Tools\MSVC\1439~1.335\bin\Hostx64\x64\link.exe /nologo CMakeFiles\test.dir\test.cpp.obj  /out:test.exe /implib:test.lib /pdb:test.pdb /version:0.0 /machine:x64 /debug /INCREMENTAL /subsystem:console  C:\bridge\lib\libremidi.lib  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
LINK Pass 1: command "C:\PROGRA~1\MIB055~1\2022\COMMUN~1\VC\Tools\MSVC\1439~1.335\bin\Hostx64\x64\link.exe /nologo CMakeFiles\test.dir\test.cpp.obj /out:test.exe /implib:test.lib /pdb:test.pdb /version:0.0 /machine:x64 /debug /INCREMENTAL /subsystem:console C:\bridge\lib\libremidi.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:CMakeFiles\test.dir/intermediate.manifest CMakeFiles\test.dir/manifest.res" failed (exit code 1120) with the following output:
   Creating library test.lib and object test.exp
libremidi.lib(observer.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiOutGetNumDevs referenced in function "protected: class std::vector<struct libremidi::output_port,class std::allocator<struct libremidi::output_port> > __cdecl libremidi::observer_winmm::get_port_list<0>(void)const " (??$get_port_list@$0A@@observer_winmm@libremidi@@IEBA?AV?$vector@Uoutput_port@libremidi@@V?$allocator@Uoutput_port@libremidi@@@std@@@std@@XZ)
libremidi.lib(midi_out.cpp.obj) : error LNK2001: unresolved external symbol __imp_midiOutGetNumDevs
libremidi.lib(observer.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiOutGetDevCapsW referenced in function "void __cdecl libremidi::MakeUniqueOutPortName(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,unsigned __int64)" (?MakeUniqueOutPortName@libremidi@@YAXAEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@_K@Z)
libremidi.lib(midi_out.cpp.obj) : error LNK2001: unresolved external symbol __imp_midiOutGetDevCapsW
libremidi.lib(observer.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiInGetNumDevs referenced in function "protected: class std::vector<struct libremidi::input_port,class std::allocator<struct libremidi::input_port> > __cdecl libremidi::observer_winmm::get_port_list<1>(void)const " (??$get_port_list@$00@observer_winmm@libremidi@@IEBA?AV?$vector@Uinput_port@libremidi@@V?$allocator@Uinput_port@libremidi@@@std@@@std@@XZ)
libremidi.lib(midi_out.cpp.obj) : error LNK2001: unresolved external symbol __imp_midiInGetNumDevs
libremidi.lib(observer.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiInGetDevCapsW referenced in function "void __cdecl libremidi::MakeUniqueInPortName(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > &,unsigned __int64)" (?MakeUniqueInPortName@libremidi@@YAXAEAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@_K@Z)
libremidi.lib(midi_out.cpp.obj) : error LNK2001: unresolved external symbol __imp_midiInGetDevCapsW
libremidi.lib(midi_out.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiOutOpen referenced in function "public: class stdx::error __cdecl libremidi::midi_out_winmm::do_open(unsigned int)" (?do_open@midi_out_winmm@libremidi@@QEAA?AVerror@stdx@@I@Z)
libremidi.lib(midi_out.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiOutClose referenced in function "public: virtual class stdx::error __cdecl libremidi::midi_out_winmm::close_port(void)" (?close_port@midi_out_winmm@libremidi@@UEAA?AVerror@stdx@@XZ)
libremidi.lib(midi_out.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiOutPrepareHeader referenced in function "public: virtual class stdx::error __cdecl libremidi::midi_out_winmm::send_message(unsigned char const *,unsigned __int64)" (?send_message@midi_out_winmm@libremidi@@UEAA?AVerror@stdx@@PEBE_K@Z)
libremidi.lib(midi_out.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiOutUnprepareHeader referenced in function "public: virtual class stdx::error __cdecl libremidi::midi_out_winmm::send_message(unsigned char const *,unsigned __int64)" (?send_message@midi_out_winmm@libremidi@@UEAA?AVerror@stdx@@PEBE_K@Z)
libremidi.lib(midi_out.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiOutShortMsg referenced in function "public: virtual class stdx::error __cdecl libremidi::midi_out_winmm::send_message(unsigned char const *,unsigned __int64)" (?send_message@midi_out_winmm@libremidi@@UEAA?AVerror@stdx@@PEBE_K@Z)
libremidi.lib(midi_out.cpp.obj) : error LNK2019: unresolved external symbol __imp_midiOutLongMsg referenced in function "public: virtual class stdx::error __cdecl libremidi::midi_out_winmm::send_message(unsigned char const *,unsigned __int64)" (?send_message@midi_out_winmm@libremidi@@UEAA?AVerror@stdx@@PEBE_K@Z)
test.exe : fatal error LNK1120: 10 unresolved externals
ninja: build stopped: subcommand failed.

There is probably something obvious that I am missing. Any help?

jcelerier commented 5 months ago

ah, if you are linking through find_package, the target should carry over winmm. this is a bug, I don't know why CMake didn't add it to the config, it should...

What you can do right now is try to add winmm here:

target_link_libraries(test PRIVATE "${LIBREMIDI_LIB}" winmm)

jcelerier commented 5 months ago

oh wait, you are doing find_library, not find_package this is why. When you link against a static library directly, it's your responsibility as end-user to link to all the dependent libraries. CMake find_package automates this step but if you do it manually through find_library, you have to pass them yourself to the linker. So adding winmm to target_link_libraries is in your case the correct thing to do.

jcelerier commented 5 months ago

Another option is to create a .dll of libremidi instead of a static library - if you use a shared library instead of a static library, it already knows which other libraries it should link to. You can do this by passing -DBUILD_SHARED_LIBS=1 when building libremidi itself.

cole-io commented 4 months ago

Thank you!