Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMakeLists INSTALL DESTINATION and INSTALL_INTERFACE are inconsistent #384

Open
cboulay opened this issue May 17, 2024 · 5 comments
Open

Comments

@cboulay
Copy link
Contributor

cboulay commented May 17, 2024

Previously, I had been grabbing concurrentqueue with cmake's FetchContent and it worked great. Recently, I switched to using it as a bitbake recipe in an embedded system, where concurrentqueue gets installed as a system-level dependency. I suddenly had to change my includes to #include <moodycamel/concurrentqueue.h>. And of course this fails when I want to build without the system-level install for local debugging without the embedded sysroot.

I think the problem is simple:

target_include_directories(${PROJECT_NAME}
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/>
)

and

install(
FILES
blockingconcurrentqueue.h
concurrentqueue.h
lightweightsemaphore.h
LICENSE.md
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/moodycamel
)

should match.

I propose removing the trailing moodycamel from the install destination.

That way, all the include statements in the source code will be the same (#include <concurrentqueue.h>) whether someone grabs concurrentqueue with FetchContent or they install concurrentqueue to the system and use find_package(concurrentqueue) followed by target_link_libraries(${PROJECT_NAME} concurrentqueue::concurrentqueue).

Currently, I'm patching concurrentqueue on fetch with this exact change and it's working well for me.

@cameron314
Copy link
Owner

I regret accepting PRs for CMake support, it seems to cause no end of issues for what is a header-only library.

It seems like if I change this, it would break code currently including <moodycamel/concurrentqueue.h>? Also, what if there is another concurrentqueue.h from some other project? Shouldn't the moodycamel be present for both in order to avoid conflicts?

@cboulay
Copy link
Contributor Author

cboulay commented May 20, 2024

I regret accepting PRs for CMake support, it seems to cause no end of issues for what is a header-only library.

I appreciate it being there. It has proven itself useful for me in a couple projects already. Nothing that I probably couldn't do with some custom CMake code but when I'm pulling in 5+ 3rd party dependencies, it's nice when most of them just need a FetchContent statement and nothing else custom.

Also, what if there is another concurrentqueue.h from some other project?

Are you concerned about overwriting in the system /usr/include?
That isn't an issue with this change. The 3 header files get installed into /usr/include/concurrentqueue/ instead of /usr/include/concurrentqueue/moodycamel where they are installed in the current version of the code. IMO, the current format is pretty strange that the org comes after the project name.

Or are you concerned about shadowing #include <concurrentqueue.h> statements?
All the examples use #include "concurrentqueue.h" so that might already be an issue.
For cmake-installed workflow users, concurrentqueue isn't available to #include unless the target project cmake config does target_link_libraries(..., so conflicts seem unlikely.

I'm hoping we can achieve syntax parity among the CMake-installed workflow and the clone-and-include workflow. With the proposed changes, the syntax for including concurrentqueue for CMake-installed users is aligned with the non-cmake workflow.

I'd also be happy with #include <concurrentqueue/concurrentqueue.h> syntax.
This can be achieved for CMake users by dropping ${PROJECT_NAME}/ from INSTALL_INTERFACE above.
However, the only way to get syntax parity for clone-and-include workflows would be to instruct users to -I path/to/concurrentqueue/.. or to nest it within the repo itself. Even though I think nesting it within a subfolder in the repo is a good idea, I also recognize that would be more of a disturbance to users than the other approaches.

@cboulay
Copy link
Contributor Author

cboulay commented May 30, 2024

@cameron314 , I'd be happy to put forth a PR, but I'm hoping you can help me decide on the preferred approach, if any.
I realize the issue itself as described thus far is confusing so I'll try to refocus it.

AFAIK, there are 3 main workflows to use concurrentqueue in a project:

  1. Clone and include the cloned folder.
  2. CMake FetchContent
  3. Clone and Install via cmake

Admittedly, the first workflow is far more common than the others, and that is totally appropriate because it's a header-only library (except C-API users). I would like to use the 3rd workflow for a specific project where automated auditing of dependencies is important and is much easier to do by installing it (as a yocto layer, in case you are curious).

Unfortunately, the 3rd workflow requires different include statements to the others, which is a bit tricky for documentation and for people like me who want to share any concurrentqueue-using code between build workflows.

The different include statements are:

  • #include "concurrentqueue.h"
    • or #include <concurrentqueue.h>, depending on how you add the cloned folder.
  • #include <moodycamel/concurrentqueue.h>

Is it desirable to you to unify the include statements across these different workflows?
If yes, what is the preferred include statement?

  • #include <concurrentqueue.h>
  • #include <moodycamel/concurrentqueue.h>
  • #include <concurrentqueue/concurrentqueue.h>

My preferred is the last or 2nd-last, but that would require that the header files are placed one folder deeper in the repository (e.g., <repo_root>/concurrentqueue/concurrentqueue.h). But I realize this is unlikely because, even with a version bump and an update to the documentation, this would cause a lot of pain for people who are fetching concurrentqueue master in their builds. That's why in my first message, the suggested change unified on the first include statement.

For now, I can solve my problem by patching concurrentqueue when I pull it, or manually adding the /usr/local/include/concurrentqueue/moodycamel path to my includes.

@cameron314
Copy link
Owner

I agree that the include paths should be consistent.

Are you concerned about overwriting in the system /usr/include?

Installing into non-namespaced directories can create conflicts. What if someone else writes a foobar::ConcurrentQueue also installed under concurrentqueue/concurrentqueue.h?

Hence, my preferred installation structure would be moodycamel/concurrentqueue/concurrentqueue.h. However, I'm okay with any of the proposed options, since I recognize that minimizing friction for existing workflows is probably more important than hypothetical clashes.

Thanks for taking the time to look into this, and for bearing with me through my slow response times.

LXYan2333 added a commit to LXYan2333/simple_parallel that referenced this issue Sep 16, 2024
@LXYan2333
Copy link

LXYan2333 commented Sep 16, 2024

edit:
there are some falut in the cmake script. the try_compile only works for imported target (i.e. the moodycamel/concurrentqueue.h case)


temporary user side solution:

use cmake's try_compile command to auto detect the possible include flavor.

cmake file:

if(NOT TARGET concurrentqueue::concurrentqueue)
    add_library(concurrentqueue::concurrentqueue ALIAS concurrentqueue)
endif()

# after cmake 3.29: Alias targets to imported libraries are also supported in try_compile.
# add these code to compatible with cmake<3.29
get_property(aliased_target TARGET concurrentqueue::concurrentqueue PROPERTY ALIASED_TARGET)
if(aliased_target)
    set(concurrentqueue_link_name concurrentqueue)
else()
    set(concurrentqueue_link_name concurrentqueue::concurrentqueue)
endif()

try_compile(
    include_moodycamel_concurrentqueue
    SOURCES ${PROJECT_SOURCE_DIR}/src/try_compile/test_include_moodycamel_concurrentqueue.cc
    LINK_LIBRARIES ${concurrentqueue_link_name})

# since `try_compile` only works for imported target, if the target is added by `add_library` or `add_subdirectory`,
# the `try_compile` will always fail. since the `concurrentqueue` target always exists, we just assume the header 
# is included as `#include <concurrentqueue.h>`
if(NOT include_moodycamel_concurrentqueue)
    set(include_concurrentqueue true)
endif()

configure_file("include_concurrentqueue.h.in" "${CMAKE_BINARY_DIR}/configured_files/include/internal_use_only/include_concurrentqueue.h" ESCAPE_QUOTES)

include_concurrentqueue.h.in

#pragma once

// clang-format off
#cmakedefine include_concurrentqueue
#cmakedefine include_moodycamel_concurrentqueue
// clang-format on

#ifdef include_concurrentqueue
    #include <concurrentqueue.h>
#elifdef include_moodycamel_concurrentqueue
    #include <moodycamel/concurrentqueue.h>
#else
    #include <concurrentqueue/concurrentqueue.h>
    #warning "try to include system concurrentqueue"
#endif

test_include_moodycamel_concurrentqueue.cc

#include <moodycamel/concurrentqueue.h>

int main() {}

then add $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/configured_files/include> into the target's PUBLIC include directories. then you can use #include <internal_use_only/include_concurrentqueue.h> to include concurrentqueue everywhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants