GithubHelp home page GithubHelp logo

Question: how to use with CMake? about qtusb HOT 17 OPEN

fpoussin avatar fpoussin commented on August 16, 2024
Question: how to use with CMake?

from qtusb.

Comments (17)

petrmanek avatar petrmanek commented on August 16, 2024 1

@fpoussin Yes, importing works at the moment -- feel free to try cloning the fork yourself. 🙂

Once I get the testing and static linking to work, I will file a PR and then we can test it more rigorously.

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

I should probably clarify that the Qt-recommended way does not work at the moment. Putting the following directive in CMakeLists.txt:

find_package(Qt6
  CONFIG
  REQUIRED
  COMPONENTS Usb
  )

....produces the following error:

-- Could NOT find Qt6Usb (missing: Qt6Usb_DIR)
CMake Error at CMakeLists.txt:50 (find_package):
  Found package configuration file:

    /usr/lib64/cmake/Qt6/Qt6Config.cmake

  but it set Qt6_FOUND to FALSE so package "Qt6" is considered to be NOT
  FOUND.  Reason given by package:

  Failed to find Qt component "Usb" config file at ""

This suggests that there may be some package metadata missing.

from qtusb.

fpoussin avatar fpoussin commented on August 16, 2024

Hi,

CMake and/or Qt6 aren't supported at the moment.

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

@fpoussin Thanks for letting me know. I can confirm now that QtUsb works without any problems using Qt6's qmake. As far as CMake support is concerned, I will likely be patching that myself in the future. If you would consider a PR, I would be happy to make my work available.

from qtusb.

fpoussin avatar fpoussin commented on August 16, 2024

Glad to know it works with Qt6!
A PR would be fantastic, thanks :)

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

@fpoussin Splendid. In the meantime, I have made some discoveries:

  • If you compile QtUsb with Qt5 and QMake, both CMake & pkg-config metadata are generated in the lib/ directory as a part of the build process.
  • If you compile QtUsb with Qt6 and QMake, only pkg-config metadata are generated. The create_cmake.prf mkspec has no effect.

I suspect this is because Qt6 uses CMake internally as its default build system for all its modules (and their corresponding CMake metadata), and in Qt6 the QMake command is only a compatibility wrapper for CMake, which appears to ignore the most of .prf files. For that reason, it seems that the only way to get the build system to generate CMake metadata in Qt6 is to fully transition from QMake to CMake.

I have taken the liberty of forking the repository and converting its build system from QMake to CMake according to the official guidelines. I also used the official pro2cmake.py helper script, which is recommended by the Qt developers, and patched some of the scope-related issues manually.

The current state is as follows:

  • I can get QtUsb to build and link with Qt6 on Linux. All metadata (and other install files) appear to be generated correctly. Have not tried Windows and macOS yet, but they should work theoretically. There may be some problems with Android, however I am unable to verify that since I do not have an Android device myself.
  • Test targets compile as well but I have to pass -DQT_BUILD_STANDALONE_TESTS=ON option to CMake, otherwise they are not generated. There is an additional issue -- with this option enabled, only test targets are generated and nothing else, so the tests build but then produce linking errors because there is no QtUsb module to link with. This leads me to believe that flipping the QT_BUILD_STANDALONE_TESTS flag may not be the proper way to enable QTest in Qt6. Unfortunately, I have no way to verify as Qt6's CMake macro documentation is virtually non-existent thus far.
  • Static build is almost fully ported. Instead of using -static target, you just need now to pass -DQTUSB_STATIC=ON flag to CMake at the configuration stage. I also implemented the correct generation of qusbglobal.h in static/dynamic scopes but there are again some problems with the Qt6's extension macros. I will look into that in the near future.
  • I verified that the generated CMake metadata can work for user projects. I generated and installed local package and tried to include it in a hello world executable using find_package(Qt6 REQUIRED COMPONENTS Usb). This worked nicely! The only catch was that I had to version-match the entire project's version with my Qt installation. Otherwise this line in the generated metadata file points to a non-existing file (that is usually provided by the Qt6 installation). At the moment, I am aware of no workaround.

from qtusb.

fpoussin avatar fpoussin commented on August 16, 2024

Great!
Can the project still be imported into another one? (what the .pri did)
This is one of the most importants aspects.

from qtusb.

fpoussin avatar fpoussin commented on August 16, 2024

Hi @petrmanek,
Have you made any progress?

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

@fpoussin I have made some progress, yes.

Here's what's new:

  • The project now builds without errors in all configurations on Linux x86_64, Intel-based macOS and Windows x86_64. No idea about Android, though, as I am unable to test it.
  • I had to enable linking with on-board libusb and hidapi on macOS, as it turns out that they are not available on vanilla macOS installations (albeit they can be downloaded with 3rd party package managers like Homebrew/Macports).
  • Both static and dynamic builds now work properly, and are toggled by CMake's standard BUILD_SHARED_LIBS option.
  • I corrected some errors & warnings arising from using slots instead of the Q_SLOTS macro in the code.
  • I had some troubles incorporating QtUsb into CMake projects via ExternalProject. Seems like Qt's internal macros do not like Debug targets and tend to reconfigure the entire parent project's cache, which is incredibly inconvenient. To avoid it, I added an option to build QtUsb as a Qt library rather than a module. When enabled by setting QTUSB_MODULE=OFF, this produces a target called ExtUsb instead of QtUsb (the name change is because of windeployqt, which confuses any DLL that begins with Qt with a Qt module, and crashes hard if it cannot be found in %QT_DIR%).

I would be interested in your feedback. The fork is publicly available for you to try. 🙂

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

@fpoussin Have you had a chance to review my work?

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

One off-topic improvement I just made while testing the library with my (Linux) laptop: I discovered & corrected a bug that disrupts control transfers. As far as I can tell, the bug has been in QtUsb long before I came, and affects all control transfers initiated through QUsbEndpoint.

The main symptom is an infinite loop of the background thread after initiating a control transfer (note that it may only be visible if you increase QtUsb's verbosity to debug level). The first transfer technically succeeds, but the infinite loop between libusb and a callback in QtUsb unnecessarily eats memory, CPU time, and effectively prevents any further control transfers from taking place.

The bug appears due to mismatch between the length and actual_length fields of libusb_transfer. While usually these fields should match values for most completed transfers, control transfers are special snowflakes. Their data is divided into two parts:

  1. A fixed setup stage of size LIBUSB_CONTROL_SETUP_SIZE (8 bytes),
  2. An optional data stage of arbitrary size.

Since the setup stage is always present, libusb does not bother to include its size in actual_length. On the other hand, the length field is always set to include the setup stage size when a control transfer is initiated. This implies that even successful and fully completed control transfers will appear as if they were missing 8 bytes of data, which will lead the callback to spin indefinitely back & forth between libusb and QtUsb. I corrected the problem by simply adding an exception for control transfers that takes these extra 8 bytes into account. Having tested the library with a USB serial modem of CDC ACM class, I can confirm that control transfers are now fully operational.

from qtusb.

fpoussin avatar fpoussin commented on August 16, 2024

Nice catch, thanks!

I've had a look, everything looks good so far :)

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

One more random catch. During testing with macOS I discovered that a call to QUsb::devices leads to non-deterministic crashes with the following backtraces:

* thread #21, name = 'QThread', stop reason = signal SIGABRT
  * frame #0: 0x00007fff20340792 libsystem_kernel.dylib`__abort_with_payload + 10
    frame #1: 0x00007fff203421d9 libsystem_kernel.dylib`abort_with_payload_wrapper_internal + 80
    frame #2: 0x00007fff2034220b libsystem_kernel.dylib`abort_with_payload + 9
    frame #3: 0x00007fff202a571b libsystem_c.dylib`_os_crash_fmt.cold.1 + 55
    frame #4: 0x00007fff20238105 libsystem_c.dylib`_os_crash_fmt + 154
    frame #5: 0x00007fff22b65cdd IOKit`IOHIDManagerScheduleWithRunLoop + 302
    frame #6: 0x0000000113762c56 libExtUsb.dylib`hid_enumerate [inlined] init_hid_manager at hid.c:382:3 [opt]
    frame #7: 0x0000000113762c05 libExtUsb.dylib`hid_enumerate [inlined] hid_init at hid.c:406 [opt]
    frame #8: 0x0000000113762bd1 libExtUsb.dylib`hid_enumerate(vendor_id=0, product_id=0) at hid.c:557 [opt]
    frame #9: 0x00000001137564f3 libExtUsb.dylib`QUsb::devices() at qusb.cpp:332:16 [opt]
    frame #10: 0x0000000113756027 libExtUsb.dylib`QUsb::QUsb(this=0x0000000103ed7070, parent=<unavailable>) at qusb.cpp:237:21 [opt]
    frame #11: 0x00000001132b7227 libusb_tools.dylib`UsbDiscoveryWorker::beginMonitoring(this=0x0000000103ed58d0) at discovery_worker.cpp:15:14
    frame #12: 0x00000001132b6e45 libusb_tools.dylib`UsbDiscoveryWorker::qt_static_metacall(_o=0x0000000103ed58d0, _c=InvokeMetaMethod, _id=2, _a=0x0000000103ed5f98) at moc_discovery_worker.cpp:100:21
[...]

Turns out that the issue was caused by multi-threading (in my program, two threads could sometimes independently call QUsb::devices roughly around the same time. I found that internally the function calls hidapi's hid_enumerate, which according to this comment is not thread-safe in macOS. For this reason, I added an application-wide mutex in the areas that use hid_enumerate and verified that it indeed prevents the crash.

from qtusb.

 avatar commented on August 16, 2024

Hi, I'm currently trying to use this library with CMake build system, does integration with this build system still in roadmap ?

Thank you for all your work !

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

@leger50 Feel free to check out my fork. The last time I used it the CMake integration worked just fine with Qt6. As to the other issues (re: multi-threading), I can report no progress.

from qtusb.

evan-swinney avatar evan-swinney commented on August 16, 2024

@petrmanek Did your CMake changes ever get merged into this repo? I tried adding this to a new Qt 6.5.0 build and it never could find the headers for the library by doing a simple add_subdirectory(QtUsb)

from qtusb.

petrmanek avatar petrmanek commented on August 16, 2024

@evan-swinney Yes, they did.

I am currently using QtUsb under Qt 6.4.3 and Qt 6.5.0 without any issues. Here is how I do it: To remove the need for installing QtUsb as a full Qt module ahead of time (and thus polluting my Qt installation), I use the QTUSB_MODULE=OFF option, which turns QtUsb into a conventional Qt library called ExtUsb (because QtXXX prefix is reserved only for Qt components and windeployqt tends to complain about that). Then I simply link my program with it using an alias target.

Here is my minimal example:

# (omitting standard CMake code for finding Qt)

set(QTUSB_MODULE OFF CACHE BOOL "" FORCE)
add_subdirectory(qtusb) # this assumes that QtUsb is cloned into ./qtusb
add_library(Qt::Usb ALIAS ExtUsb)

add_executable(my_program main.cpp)
# (omitting linking with Qt and other libraries)
target_link_libraries(my_program PUBLIC Qt::Usb)

In main.cpp, I can then use QtUsb as desired, e.g.:

#include <QUsbDevice>
#include <QUsbEndpoint>

int main(int argc, char* argv[]) {
  auto device = new QUsbDevice();
  // this should compile
}

Hope this helps!

from qtusb.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.