depthai-core: unexpected use of CMAKE_TOOLCHAIN_FILE in cmake, recommend not using it
The current wipe of CMAKE_TOOLCHAIN_FILE
in the project’s CMakeLists.txt is unexpected. As coded today, its only action is to set(CMAKE_POSITION_INDEPENDENT_CODE ON)
when BUILD_SHARED_LIBS=ON
.
It is my understanding that CMAKE_TOOLCHAIN_FILE is for other uses. As per cmake docs
path to a file which is read early in the CMake run and which specifies locations for compilers and toolchain utilities, and other target platform and compiler related information
For example, I need to set CMAKE_TOOLCHAIN_FILE to my vcpkg installation so that this project can find that managed installation of OpenCV which is compiled for specific variant, flags, etc. This is done by specifying CMAKE_TOOLCHAIN_FILE on the command line as a flag to cmake (or in settings of an IDE that are passed to cmake).
Setup
- from the develop branch 9719f55fba6c98f57151025d849ed2fdd139bf08
Recommendation
Move set(CMAKE_POSITION_INDEPENDENT_CODE ON)
to a place unrelated to CMAKE_TOOLCHAIN_FILE. In my view, it is unrelated to the cmake toolchain feature and instead a specific compiler/linker need of the depthai-core project like specifying the c++11 standards with no extensions, or pthread preference.
Workaround
Always edit this project’s CMakeLists.txt
to remove this wipe of CMAKE_TOOLCHAIN_FILE and replicate the set(CMAKE_POSITION_INDEPENDENT_CODE ON)
elsewhere if shared libraries are built.
About this issue
- Original URL
- State: open
- Created 3 years ago
- Comments: 18 (9 by maintainers)
Please read the following in a neutral manner. I do not intend any insults. I am writing with an intention to detail my perspective and seek a solution that helps the most people. That is usually my goal. I created solutions for a billion people when I was a Microsoft employee. That experience causes me to have-no-fear, to solve hard problems, and solve problems for the most people.
Shifting the problem to someone else
My primary effort is developing apps for artists. I am the bridge between artists and complex technology. Yes, I can also dive deep into memory and threading bugs in libraries. 😉 I need my dependencies to be reliable and mange their own internals. I have no desire to dig into the internals of a dependency. It is not my job/role.
Unfortunately, I sometimes have to do the work that should be done by a dependency. I’ve done it for both OpenCV and DepthAi projects. (Remember, no blame/insults.) Going into the internals of another team is usually not a good thing. Me having to: 1) hack DepthAI’s cmake files because, 2) DepthAi and OpenCV don’t manage their own internal dependencies, because 3) OpenCV uses custom symbolic names for DLLs, and 4) OpenCV doesn’t distribute the cmake files that create these symbolic names it uses itself…ugh. No good here. 😝
All of that means I (app developer) have to review/correct the internals of OpenCV’s cmake infrastructure, write my own cmake files to match OpenCV’s internals, and hack DepthAI and/or Hunter. Only then can I do my job which is to write applications for artists. I’m smart, experiences, and I fixed all this myself. But it takes days to weeks of my time. Time is something that can never be replaced; the most valuable thing in the world. And this only fixes it for me; not the other 7.8 billion people in the world.
There is also another shift-the-problem in this discussion. I assert that shared libraries shift problems to end-users. End-users (like my artists) have problems when they don’t have the exact shared libraries needed at runtime. The shared libraries are installed/replaced/removed by system upgrades, other app installs, PATH changes, and this is well-known on Windows as “DLL Hell”. Everyone with experience has seen this. End-users usually can not fix this – they do not have the knowledge/capability.
OpenCV example
Please remember…no insults. I am not debating the pro/cons of static-vs-shared libraries. Instead, I want to examine/inspect the fears that both OpenCV and DepthAi are expressing. And I need to use static/shared libraries as it is part of this fear.
@alalek, who are you protecting? And what you protecting them from? I request you clearly document this.✍
You write, “You would get linker errors, or crashes in runtime later.” When libraries are linked together, the linker immediately reports ABI problems. That’s how a linker works. This is not a problem and nothing to fear. When a compiled/linked app is tested…errors/crashes are the goal. This is not a problem and nothing to fear. This “linker error, crashes” you write happens with both static and shared libraries. It is not unique to one or the other.
When OpenCV is compiled both as a shared-library (DLL) or as a static library it continues to produce the “linker errors, crashes” problem that alalek writes. How? Because that’s how linkers work. They report ABI problems when they link. Crashes later due to API problems are also in both shared and static – because OpenCV.DLL and OpenCV.LIB do not have all code linked within them. At runtime, both static OpenCV.LIB or shared OpenCV.DLL themselves use shared-libraries (c/c++ runtimes, all Windows functionality, ffmpeg, intel media decoders, graphics drivers, onnxruntime, tbb, ipp, cuda, opencl runtime, etc.). None of the code I listed is contained within OpenCV.LIB or OpenCV.DLL. Both can have runtime crashes.💥
The following static and shared library examples have equal chance of success or failure. I see no reason for fear.
Distribution of static libraries is similar to distribution of shared libraries. Both are files on a hard drive. Both benefit from versioning. Both can be distributed with packages (linux rpm/deb, windows msi/exe, vcpkg, chocolately, etc.) and all those package tools have dependency management. All compilers and linkers have options to override default search paths for libraries and full path names to libraries can always be used. Both cmake
find_dependency()
andfind_package()
have versioning capability. If something specific is needed, then projects should specify it.Is OpenCV suggesting that OpenCV has a problem when a random person on the Internet downloads OpenCV source code, compiles it, and posts a ZIP file of the OpenCV.LIB or OpenCV.DLL with no versioning, packaging, or cmake? Is this random developer the primary focus of the OpenCV team? Even if this was user#1, this random ZIP download of OpenCV.DLL or OpenCV.LIB both have 3rd party external dependency challenges and both require those dependencies be installed before a compile/link/run will succeed.
Libraries (both shared and static) provide their own ABI/API guarantees. Compiling with a different version LIB, or having a different shared library/DLL installed, are both challenges. Though, I believe the shared library/DLL is worse due to DLL Hell.🥵
My focus is on my app…not OpenCV. When I compile my app and link to static OpenCV, I am guaranteed to have that exact version of OpenCV because it is linked/contained within my app. I can test it. In contrast, if I link to shared-library OpenCV.DLL, I can test it but I can’t easily control what version of OpenCV.DLL is found on the end-user’s computer. Proof? 🔍 One of the most common complaints and problems with OpenCV on the internet is “Which version of the OpenCV DLL works with this app, 32/64-bit, os version” and “How do I install the OpenCV Dlls”. Do a quick search with Google. End-users have shared-library “OpenCV.DLL Hell”.
DepthAi example
Depthai, yesterday proved that the fear alalek writes applies to both shared and static libraries. I found a mutex bug in DepthAi where the mutex was not being locked. Correctly locking the mutex causes a chain reaction that exposes bugs in other code. The ABI and API are compatible before and after my mutex fix. However, if depthai was compiled as a shared-library, then all apps which linked to a shared DepthAi library would now randomly crash due to timing/race conditions. If DepthAi was compiled static, then the apps wouldn’t crash because they would continue to use the older code that has the mutex bug that the app’s code was lucky to not expose. You can easily reproduce this using DepthAi’s own ctest suite of tests. Before fix, all pass. After fix, 1 to 3 will usually fail due to timing/race conditions.
Depthai, you will also have OpenCV.DLL hell. Some developer will use shared-library version=4.5.6 OpenCV.dll to build depthai-core.lib. This means anyone that links to that depthai-core.lib will need a version of OpenCV.dll that is 100% compatible with version 4.5.6. Then the same dev (or another dev) will write an app that uses both OpenCV and DepthAi apis, and compile/links to version=4.0.1 OpenCV.dll to build their app. Naturally, this means the app attempts to link to both depthai-core.lib and the OpenCV.dll library stubs. This is OpenCV.DLL hell because you can’t have two different versions of OpenCV.dll linked/loaded in the same app. OpenCV might say “we are ABI/API compatible” but that is not true (because OpenCV had bugs, fixed bugs and added/changed features) and now the shared-library OpenCV.DLL exhibits the same problems alalek wrote above “linker errors, or crashes in runtime later”.
Test as soon as possible
Is ABI and API compatibility something to fear? No. All projects that matter will test for ABI/API compatibility issues. And those ABI/API compatibility issues exist for BOTH shared and static libraries.
All projects that want reliability do testing. The remaining untested random projects need automation to manage internals/dependencies. These random projects need to be able to do a simple
cmake --build
. They should not do the 4-step reverse-engineering and hack that I describe above; and they might not have the capability to do it.So what is this fear of static libraries? And who is being protected?🤔
Static libraries are tested at compile/link-time when developers and testers are actively examining and testing. My testers have problems. Good! That is their job! It keeps the problem with the testers, not my end-user artists. Shared libraries should also test at compile/link-time. But they can’t test the end-user’s computer because of OpenCV.DLL Hell.
Shifting Redux
From what I can discern, this aversion to static libraries is an attempt to shift the responsibility of solving problems from one’s project to downstream projects. And in my scenario, each of you (OpenCV and DepthAi) have cascaded that to my project and the other dev that reported the above linked issue. I am confident we are not the only two in the world. OpenCV doesn’t want to do it. DepthAi doesn’t want to do it. So now I and other app devs have to do it. And we have to reverse-engineer the logic and internals of both OpenCV and DepthAI. 😞
Who are you both protecting?
find_package(ceres 2.0.3 REQUIRED)
.We devs appreciate the functionality your libraries provide. Thank you. 🙂 For some like DepthAi we directly pay money. Others like OpenCV are indirectly paid through sponsorships from companies like Intel and we buy Intel hardware. There is compensation for the effort occurring.
Unfortunately, your projects are making it harder for us developers. Giving us vague protection for something we don’t want. I know you are not doing this with malice. Both your teams have demonstrated a willingness to make things better. I suspect your projects’ fears above are based in outdated rumors rather than current fact. Is this fear based on past/outdated use of static libraries before today’s versioning, package control, and modern cmake? Or perhaps it is self-protection. Are you trying to protect your own dev/test resources so they don’t have to update your cmake code? Or, are you trying to protect your support teams from something?
Need user scenarios
I request you write out a full user scenario. This is usually 2-3 paragraphs. Describe this “person” that you are protecting. Describe two specific problem scenarios of this person. The ten-word-fear that alalek writes above is too short and vague to be actionable or follow thinking/logic.
With clear user scenarios, we as a group can probably find a solution that works for us and the most people. That is my goal. Remember, I’ve already hacked a workaround for my own project. I want to fix it for the other 7.8 billion in the world.
I agree - I think CMake does offer everything needed to correctly define all dependencies, between libraries, etc… Although I think that the pain point is distribution of static libraries (shared libraries seem to be given more thought).
That said, regarding original issue, I’ll try to find some time to play around with the toolchain issue. Any pointers on this or minimum example which isolates the issue? Otherwise will try to create a similar case locally
There is strong recommendation to use shared builds instead. Redistribution of “static builds” are over-complicated task for projects with external dependencies. Just
find_dependency()
approach may work until that provides the same results (you can play withfind_dependency(LAPACK)
on different user systems). Much worse case, this automatic approach may silently find a different version of binaries which is not compatible with used dependency headers in compiled binaries. You would get linker errors, or crashes in runtime later.Unfortunately there is no robust easy-to-use solution for “static builds” problem. In OpenCV project we don’t add anything to “static builds”, so advanced users should configure dependencies yourself (with full responsibility) before calling
find_package(OpenCV)
of “static builds”. Such configurations are specific for their use case only, build environment and used options/dependencies.@diablodale please point me to modern CMake projects / examples properly doing transitive static dependencies. I’m wondering how are they are passing down these dependencies, how well it plays with other CMake projects, etc… I’d gladly take a look as I too had a bunch of static library problems in the past (although before modern CMake came to be the norm).
As of right now, they way our library handles static dependencies is by installing them alongside. So compiling DepthAI statically (and disabling optional opencv support, as we do not handle OpenCVs dependency in same manner as our internal ones) will install all needed dependencies along side of it, making a
find_package(depthai)
correctly resolve everything needed to compile it into your project. This works for shared compilation as well.I’m not sure if our way is correct, pretty sure it wouldn’t play well with installing things to system directories, but for starters that is the way I chose to do it.
I agree with @alalek on this as far as my limited insight in various different projects reach. Static dependencies are usually not handled transitively. (offtopic, in our project we do install to build directory by default and install the static dependencies that were used. This enables the option of linking to statically built depthai, and all dependencies will be resolved to ones that were installed alongside. This isn’t quite doable in case when you want to install to a public location, with public/user controllable dependencies, but okay for isolated case like ours IMO)
Having
find_dependencies
(find_package
) in *Config seems okay, but would have to be done only for static builds. Shared have dependencies already collapsed. On one hand it seems reasonable for a project to also specify the location of its dependencies it used for itself, but given that installation main point is to redistribute, it doesn’t make much sense (maybe if those were packaged together). That issue is then similar to runtime question: every app self contained vs dependent on other libraries outside itself - but for build time.@diablodale WRT
HunterGate
call, if you want you can create an MRE and I’ll take a look, but its likely better to be addressed to Hunter project instead.About the:
I meant not to recreate OpenCVs Config file but to “extend” it with dependencies. That’d mean 1 include line and the rest are the same lines that you’ve posted above:
But
toolchain
way is probably betterhaha, the plot thickens. 🧐 We might have confluence of issues. Please do review the below. I would like to reach a consensus on the problem and its cause. Afterwards, we can work on a solution. I’m including @alalek from the OpenCV project as he has direct knowledge and I’m starting to think OpenCV owns part of a solution.
My learning on this topic has been via cmake official docs, random articles found with searches, and discussions in context of projects (e.g. github issues). As I was doing a google search for suggested reading, I ran across https://cmake.org/cmake/help/git-stage/guide/importing-exporting/index.html
Which is the opposite of what I previously learned and wrote above in this issue. At first, my thought was…what does “found” mean? Does it mean only checking the xxx_FOUND variable? Does it mean actively searching for files on the hard drive? etc. So I did more reading at that URL and at https://cmake.org/cmake/help/latest/module/FindPackageHandleStandardArgs.html
https://cmake.org/cmake/help/git-stage/guide/using-dependencies/index.html
https://cmake.org/cmake/help/latest/command/find_package.html
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/
Then I referenced a discussion in OpenCV https://github.com/opencv/opencv/issues/19282 and found also https://github.com/opencv/opencv/issues/18521. The first was a trigger for my viewpoint above. The latter I found today. Combining all that then made me question my previous views formed by the OpenCV issue. I can be wrong, and I prefer to learn and correct. 🔬
As a test, I traced the scenario that it is OpenCV’s job to actively search on hard drives for dependencies it needs itself. With a shared library build (e.g. Windows DLL), most everything needed is found at compile-time of OpenCV itself and everything collected and linked together in one gigantic DLL. All code needed to run OpenCV is within that one DLL.
In contrast, a static library builds of OpenCV creates a Windows .LIB file. That .LIB file contains only the code of the OpenCV project itself. It does not contain the code of its dependencies (e.g. eigen, ceres, IPP, etc.). Generally for static libraries, the linker does not combine the code of the project with the code of the project’s dependencies into one gigantic static LIB file. The linker, in downstream projects that use the static build of OpenCV, needs to link to the code for OpenCV’s dependencies. And cmake provides mechanisms for this, e.g. target_link_libraries() with transitive dependencies.
When these dependencies are known by the cmake infrastructure of OpenCV, then somehow that knowledge needs to be given to downstream projects that link to OpenCV. The built-in technology to do this is https://cmake.org/cmake/help/git-stage/guide/importing-exporting/index.html#creating-a-package-configuration-file
I searched the entire OpenCV source code from the 4.5 branch. There is no code that calls
find_dependency()
. I also searched an install of OpenCV. It also does not have any call tofind_dependency()
. Referencing the official cmake docs above…suggests to me that it is OpenCV’s job to find all its dependencies in itsOpenCVConfig.cmake
file. I don’t think OpenCV is doing what it needs to be doing. This aligns with the issue https://github.com/opencv/opencv/issues/18521 and underlies issue https://github.com/opencv/opencv/issues/19282Perhaps then a confluence of issues contribute. Please do check each of these for correctness.
There may also be issues within depthai project or hunter, but these are later problems that might be sideaffects of the above six.
OK. Enough for today. What are all your viewpoints? 🙃