OpenSceneGraph plugin not included in Conan? - c++

I'm trying to do simple mesh viewer using OpenSceneGraph and I want to use Conan for dependencies.
The compilation is working well in both debug and release mode (I'm compiling on Windows for now with msvc toolchain).
As soon as I try to load any kind of mesh, the osgDB::readNodeFiles just fail. Looks like the plugin are not linked to the final binary.
I checked in the Conan's package, the plugin list of .lib exists and are supposed to be linked I guess.
What could I miss ?
There is my conanfile.txt :
[requires]
openscenegraph/3.6.5
[generators]
cmake
The CMakeLists.txt is also straightforward :
cmake_minimum_required(VERSION 3.17)
# Set a default build type if none was specified
set(default_build_type "Release")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
set(PROJECT_NAME 3D_radio)
project(${PROJECT_NAME})
if(EXISTS ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
set(CMAKE_CONFIGURATION_TYPES ${CONAN_CONFIGURATION_TYPES})
else()
message(WARNING "The file conanbuildinfo.cmake doesn't exist, you have to run conan install first")
endif()
# get all source files
file(GLOB all_SRCS
"include/*.h"
"source/*.cpp"
)
# add executable and addShader libraries
add_executable(${PROJECT_NAME} ${all_SRCS} source/main.cpp include/main.h)
target_include_directories(${PROJECT_NAME} PRIVATE
include
${CONAN_INCLUDE_DIRS}
)
target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
And the code is also very simple :
int main(int argc, char **argv) {
// use an ArgumentParser object to manage the program arguments.
osg::ArgumentParser arguments(&argc,argv);
// read the scene from the list of file specified commandline args.
osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFiles(arguments);
// if not loaded assume no arguments passed in, try use default mode instead.
if (!loadedModel) loadedModel = osgDB::readNodeFile("cow.osgt");
// if no model has been successfully loaded report failure.
if (!loadedModel)
{
std::cout << arguments.getApplicationName() <<": No data loaded" << std::endl;
return 1;
}
}
The console output :
C:\...\cmake-build-release\bin\program.exe C:\...\first.obj
C:\...\cmake-build-release\bin\program.exe: No data loaded
Error reading file C:\...\first.obj: read error (Could not find plugin to read objects from file "C:\...\first.obj".)
Error reading file cow.osgt: read error (Could not find plugin to read objects from file "cow.osgt".)
I have no problem to open those files in Paint 3D for example. So I know they are correct. The problem looks to come from linking. Any idea ?

I found the solution.
I'm compiling statically the program, the plugins are linked to the final binary but the plugin registry look for a dynamic library (osgdb_obj.dll for example).
If you have the same problem, you have to register manually the plugin :
USE_OSGPLUGIN(obj)
USE_GRAPHICSWINDOW() // and you may want this to get a window

The "plugin list of .lib" which you highlighted are, unlike static libs, import libraries which will introduce a dynamic DLL dependency in the final link. The plugin DLLs still have to be present somewhere from where the linker can then load them off, at runtime. OSG plugins are typically loaded relative to the value of OSG_LIBRARY_PATH env variable. Setting this to the folder holding the, say "osgPlugins-3.6.5" directory, should successfully load the plugins.
SET OSG_LIBRARY_PATH=C:\TOOLS\vcpkg\installed\x64-windows\debug\plugins\
Few things which could still break this are: Debug/Release plugins mismatch and some spurious space in the OSG_LIBRARY_PATH env variable, like this one (logged via SET OSG_NOTIFY_LEVEL=DEBUG)
FindFileInPath() : trying C:\TOOLS\vcpkg\installed\x64-windows\debug\plugins\ \osgPlugins-3.6.5\osgdb_osgd.dll
The space above was present in the OSG_LIBRARY_PATH env

Related

What's a simple straightforward find_package set of files for CMake

I'm new(ish) to CMake (meaning off and on over a few years I've been forced to use it even though it's something that's made me question my career path).
I have a folder with an include folder, and a lib folder containing .lib files and their corresponding .dlls for a dependency I'll call "mydep". I need to provide the infrastructure files, mydep-config.cmake, mydep-target.cmake, etc, which will add the includes folders and .lib files to the command line for compiling and linking and then move the .dlls to a specific location.
Can anyone point me to a simple example anywhere on the net that illustrates the way to do this? The CMake documentation is utterly useless.
Thanks!
There are basically 2 files you need to put in the correct location on the file system. Let's assume the library is to import is called ExternLib with version 1.2.3 and compiled for 64 bit. With the and you've got it stored in your repo as
external_dependencies/lib/ExternLib.lib
external_dependencies/bin/ExternLib.dll
external_dependencies/include/ExternLib/ExternHeader.hpp
external_dependencies/include/ExternLib/...
...
and you want the include paths used in your project to be
#include "ExternLib/ExternHeader.hpp"
First of all we'll use the file names externlib-config.cmake and externlib-version-config.cmake to allow the user to use arbitrary case when using find_package.
externlib-version-config.cmake
This file is used to check, if the version of the config script is compatible with the cmake configuration trying to find the package. Adding this file allows you to place multiple versions of the library to be found for different target architectures (e.g. Windows x64, Win32, linux x86_64, ...). find_package won't read the externlib-config.cmake in the same directory, if the version is marked as non-compatible via the version file.
set(PACKAGE_VERSION "1.2.3")
# basically ignore the version passed
set(PACKAGE_VERSION_COMPATIBLE TRUE) # consider every version compatible
#usually you'd add logic for ignoring any unspecified parts of the version here instead of treating unspecified version parts as 0
# we'll keep it simple though
if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
set(PACKAGE_VERSION_EXACT TRUE) # make version as exact match
endif()
if(NOT WIN32)
set(PACKAGE_VERSION_COMPATIBLE FALSE) # disallow older version
else()
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") # note: we'll ignore the possibility of find_package before the first project() command
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()
if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") # check for 64 bit
set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()
externlib-config.cmake
Here we'll put the creation of the actual library in addition to providing any functions/macros/variables the user should have access to. We'll use ExternLib::ExternLib as target name, but you can simply replace this by any name of your choosing unique in your project.
if(NOT TARGET ExternLib::ExternLib) # prevent recreation on multiple uses of find_package
add_library(ExternLib::ExternLib SHARED IMPORTED)
get_filename_component(EXTERNLIB_BASE_DIR ${CMAKE_CURRENT_LISTS_DIR}/../../.. ABSOLUTE)
set(EXTERNLIB_BINARY_DIR ${EXTERNLIB_BASE_DIR} CACHE INTERNAL "Directory containing the dlls for ExternLib")
# add info about locations of the dll/lib files
set_target_properties(ExternLib::ExternLib PROPERTIES
IMPORTED_LOCATION "$CACHE{EXTERNLIB_BINARY_DIR}/ExternLib.dll"
IMPORTED_IMPLIB "${EXTERNLIB_BASE_DIR}/lib/ExternLib.lib"
)
# add include directory information
target_include_directories(ExternLib::ExternLib INTERFACE "${EXTERNLIB_BASE_DIR}/include")
# add dependencies, if required; you may need to use find_package to locate those
# target_link_libraries(ExternLib::ExternLib INTERFACE ...)
endif()
# any helper function/macro definitions should go here, since they may need to get reintroduced
Note: I did ignore the possibility of components of a package being specified here. Any components specifying components for find_package are simply ignored for the script above.
Placement of the files
You'll need to place the files in one of some possible paths below one of the directories mentioned in CMAKE_PREFIX_PATH or in one of the default dirs, see find_package Config Mode Search Procedure.
Note: we'll use a path that isn't documented for windows, since this path would also work for linux. (Boost is doing this too.)
lib/cmake/ExternLib-x64-1.2.3
(You could choose a different suffix to lib/cmake/ExternLib or just use lib/cmake/ExternLib. The search procedure picks up on any directory names which starts with the package name ignoring case, if it expects the lib name.)
Place both the files in this directory. externlib-config.cmake assumes lib is external_dependencies/lib here. Otherwise you may need to adjust EXTERNLIB_BASE_DIR accordingly.
Usage
We'll assume the CMakeLists.txt file is placed in the same directory as external_dependencies
project(...)
...
add_executable(my_exe ...)
...
# this allows the user to pass directories to be searched first via -D option during configuration
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies)
find_package(ExternLib REQUIRED)
target_link_libraries(my_exe PRIVATE ExternLib::ExternLib)
# allow vs debugger to find the dll without copying the dlls around
set_target_properties(my_exe PROPERTIES
VS_DEBUGGER_ENVIRONMENT "PATH=${EXTERNLIB_BINARY_DIR};$ENV{PATH}"
)

why the relative path is different when I use Cmake build and VS 2019 build?

I'm new in Cmake. And I try to use Cmake to construct my project.
In my project, I need to load some resources in runtime. for instance:
string inFileName = "../Resources/resource.txt";
// string inFileName = "../../Resources/resource.txt";
ifstream ifs;
ifs.open(inFileName.c_str());
if (ifs) {
....
}
But when I use the command line cmake ../ and cmake --build . --config Release in project/build. my file path should be relative to ${PROJEDCT_BINARY}, i.e. inFileName = "../resources/resource.txt".
But when I use cmake ../ and open the sln file with VS2019 then right-click to build and run, my file path should be relative to the executable, i.e. inFileName = "../../resources/resource.txt".
I don't know why this happened, and I search through Internet, It seems no one else encounters this stupid question...
Below is my file structure.
|--3rdParty
|----CmakeLists.txt
|--include
|----header.h
|--source
|----source.cpp
|----CmakeLists.txt
|--resources
|----resource.txt
|--CmakeLists
and my root CmakeLists.txt
cmake_minimum_required(VERSION 3.12)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(OBMI VERSION 0.1.0.0 LANGUAGES C CXX CUDA)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
add_subdirectory(3rdParty)
add_subdirectory(source)
source/CmakeLists.txt
add_executable(mSI)
target_sources(mSI PRIVATE
${PROJECT_SOURCE_DIR}/include/header.h
# source
source.cpp
)
target_include_directories(multiSpectrumImaging
PRIVATE
${PROJECT_SOURCE_DIR})
target_link_libraries(mSI
PRIVATE
...
)
When using relative paths to load files, the resolution of the final filename depends on the current working directory. The relative path is appended to that working directory. That current working directory is not necessarily the same as the path of your application; it will be the path of the surrounding environment from which the application is started (or can be set specifically for a debug environment in most IDE's).
You don't specify exactly how you run your program when you run it not from the IDE - just by double-clicking the executable maybe? You also don't tell us where the executable is built in relation to your sources?
Specifically for running from Visual Studio, you can set the working directory in the "Debugging" section of the Project Properties.
For a more flexible solution, what I typically do is to determine the path of your executable, and then appending the relative path to load resources to that.
Basically, the full executable path is stored in argv[0] (if you have a int main(int argc, char** argv) {...}, i.e., the first element of the second argument to your main function). For more information on this, see for example the answers to this other question.
By generating an MSVS solution file you (CMake) create a working environment for MSVS where the solution (and projects) is generated. So everything relative there would be relative to those files generated and as far as MSVS is concerned, that directory is the center of the world. That's why you should strive to use absolute and not relative paths.
To achieve that CMake has a bunch of variables and PROJECT_SOURCE_DIR, which you use, is one of them. But there is one which seems to suite your case better case, though: CMAKE_SOURCE_DIR.
So whenever you need to use your resources, use the following path in your CMake script: "${CMAKE_SOURCE_DIR}/resources/resources.txt"
If you need your resources to load in runtime then it goes beyond CMake and its capabilities. You should put these resources relative to your resulting binary because what place they have in the project doesn't matter anymore. CMake helps with it by providing install and file(COPY ...). Where the former is mostly used during the packaging of your application and the latter might be used during development to ease the burden.
For example, you can have the following in your project (source/CmakeLists.txt) CMake file:
file(COPY "${CMAKE_SOURCE_DIR}/resources" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
Which should place the resources folder where your binary gets created.

Qt resources not displaying when building with cmake (windows)

I have a Visual Studio Qt solution that I am moving to Cmake.
Everything is compiling and working fine, except all icons (.png) that I have in *.qrc file are not displayed at all.
My CMakeLists.txt is standard for a Qt project:
...
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
set(SOURCE_FILES ...)
set(HEADER_FILES ...)
set(UI_FILES ...)
set(RESOURCE_FILES resources/res.qrc)
add_executable(project WIN32 ${SOURCE_FILES} ${HEADER_FILES} ${UI_FILES} ${RESOURCE_FILES})
find_package(Qt5 REQUIRED COMPONENTS ...)
target_link_libraries(project PRIVATE Qt5::Core etc...)
I think that all icons are in fact embedded into .exe, because:
1. If I remove all paths from the qrc file and recompile, the .exe size is decreased by the icons size
2. I checked the qrc_*.cpp generated by AUTORCC and all the images are there.
But when I am iterating over all my resources using this code snippet
QDirIterator it(":", QDirIterator::Subdirectories);
while (it.hasNext()) {
qDebug() << it.next();
}
the icons are NOT there.
I have tried an alternative approach with qt5_add_resources() instead of AUTORCC and the result is the same.
I also have all the iconengines, imageformats, platforms, styles folders on the same path as .exe.
Edit:
Added the whole Cmake part of the project to github.
Considering the discussion we had in your post comment, CMake works as expected and the resources are loaded into your application.
As you mentioned "When I run my app from clion it shows the images. If I copy the exe to a folder with *.dll etc it doesn't.", so either:
Some files are missing when you copy them, you should try to use windeployqt to correctly copy what's needed at runtime
Or the problem comes from the way the qrc is neing loaded, then, I suggest that you debug the content of qrc_*.cpp files (your can debug it after you copied it outside clion) and see why it fails to load the resource
Anyway this looks more like a deployment issue than a configuration/compilation issue.

CMake - get_filename_component Unknown component directory

I am having a bit of trouble with CMake. I am trying to link with GLFW and include multiple source files of my own project. (I'm switching from Visual Studio to make my project cross-platform).
GLFW is in the folder deps/glfw-3.1.1 and my source code is in the folder src
Here is my CMakeLists.txt file:
# Tell CMake to use a minimum of version 3.2
cmake_minimum_required(3.2)
project(Sparky)
# TODO: Versions
# Add all of our source code to the project
file(GLOB_RECURSE Sparky_SOURCES "src/*.cpp")
file(GLOB_RECURSE Sparky_HEADERS "src/*.h")
set(Sparky_INCLUDE_DIRS "")
foreach (_headerFile ${Sparky_HEADERS})
get_filename_component(_dir ${_headerFile} path)
list (APPEND Sparky_INCLUDE_DIRS ${_dir})
endforeach()
list(REMOVE_DUPLICATES Sparky_INCLUDE_DIRS)
add_subdirectory(deps/glfw-3.1.1)
include_directories(${Sparky_INCLUDE_DIRS})
include_directories(deps/glfw-3.1.1/include)
add_executable(Sparky ${Sparky_SOURCES}
target_link_libraries(Sparky glfw ${GLFW_LIBRARIES}))
It seems that there is at least an iteration with wrong values for variables _headerFile and path. Try to print values of these variables before launching get_filename_component in the foreach loop using the following code.
message(STATUS "_headerFile: ${_headerFile} )
message(STATUS "path: " ${path} )
Sometimes these types of errors can be generated by wrong values of these parameters.
It is common to see this error with "multiple inclusions" of *Config.cmake files (e.g. you run CMake for lib_A which uses both lib_B and lib_C while lib_B also links to lib_C).
You can detect this behaviour through CMake messages:
message(STATUS "_headerFile: ${_headerFile} )
message(STATUS "path: " ${path} )
Then the output will be displayed twice or more, probably with different values each time it enters the *Config.cmake file.
You can easily solve the problem using IF statements (think of the #ifndef in C headers):
IF (MYPKG_FOUND)
# Already in cache, be silent
SET(MYPKG_FIND_QUIETLY TRUE)
ELSE (EdgeFlow_FOUND)
# do your stuff, you're here for the first time!
ENDIF(MYPKG_FOUND)
For me this error was caused by putting list into get_filename_component().
Example:
set(abc /path/first /path/second)
get_filename_component(abc_name ${abc} NAME)
The error will look like:
get_filename_component unknown component /path/second

cmake + sdl - disable sdlmain

I"m linking SDL with my application using cmake (winxp sp3, cmake 2.8.4).
cmake_minimum_required(VERSION 2.8)
find_package(SDL REQUIRED)
set(src WIN32 main.cpp)
include_directories(${QT_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR} ${SDL_INCLUDE_DIR})
add_executable(test ${src})
target_link_libraries(test ${SDL_LIBRARY})
Problem: SDL_LIBRARY contains SDLmain.lib, and I need to avoid linking with it (I already have one other library that contains main but isn't mentioned in this cmakelists.txt example).
I need to remove SDLmain entry from SDL_LIBRARY. This must be done without using hard-coded paths to the library - basically I need to keep using find_package to set up sdl-related variables, but I must ensure that SDLmain is not within SDL_LIBRARY. Also, I'm using cmake 2.8.4 which doesn't have string(FIND).
How can I do that?
Does that help?
FindSDL.cmake:
# This module responds to the the flag:
# SDL_BUILDING_LIBRARY
# If this is defined, then no SDL_main will be linked in because
# only applications need main().
# Otherwise, it is assumed you are building an application and this
# module will attempt to locate and set the the proper link flags
# as part of the returned SDL_LIBRARY variable.