I'm porting a shared library from CMake to QMake (for better integration with the rest of our codebase), and am having trouble in Windows with having qmake generate both the .lib and .dll files.
I have ported this over to Linux and MacOS (easier with Unix shared library formats), but am having trouble with Windows.
Please note that I am not using the Mingw compiler, but the MSVC 2015 compiler. Unfortunately, I can't use the Mingw compiler for various reasons.
Relevant code in my .pro file:
Qt -= core gui #not using the qt library
CONFIG += c++14 warn_on
TEMPLATE = lib
TARGET = MyLibrary
With CMake, I was able to set certain .h files as PUBLIC_HEADER files, and after running make install, it generated the proper .lib and .dll files.
But with qmake, it only generated a very tiny .dll file (<15kb). I tried adding CONFIG += static but that created a .lib file with all the symbols there, not the shared library files I am looking for.
Edit: After looking more into it, a better question could be:
Is there a qmake equivalent (or workaround) of CMake's PUBLIC_HEADER property?
In a nutshell: #Matt is right, you need to take active action in your source code to export classes, functions and other symbols. This is mandatory in Windows, but it is also a recommended practice in Unix. When the MSVC++ compiler sees exported symbols, it automatically creates both the .DLL and the .LIB import library.
To do this in a cross-platform way with qmake, a nice guide is following the pattern of the Qt Creator wizard: Welcome -> New Project -> Library -> C++ library. Project location..., name: 'testlib' (for instance). Next, next, choose your kit, next and finish. The files created by the wizard in the project directory are:
testlib.cpp
testlib_global.h
testlib.h
testlib.pro
testlib.pro.user
The interesting bits are in testlib.pro:
TEMPLATE = lib
DEFINES += TESTLIB_LIBRARY
And the header "testlib_global.h"
#ifndef TESTLIB_GLOBAL_H
#define TESTLIB_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(TESTLIB_LIBRARY)
# define TESTLIB_EXPORT Q_DECL_EXPORT
#else
# define TESTLIB_EXPORT Q_DECL_IMPORT
#endif
#endif // TESTLIB_GLOBAL_H
This header should be included in other headers, that would be using the TESTLIB_EXPORT macro like this (testlib.h):
#ifndef TESTLIB_H
#define TESTLIB_H
#include "testlib_global.h"
class TESTLIB_EXPORT Testlib
{
public:
Testlib();
// ...
};
The key is to define TESTLIB_LIBRARY in the project .pro that builds the library, so the TESTLIB_EXPORT macro is defined as Q_DECL_EXPORT, which in Windows is defined as __declspec(dllexport), and avoid defining TESTLIB_LIBRARY in the projects using the library. More about this here.
Now, the second part of your question. In CMake documentation, it says:
PUBLIC_HEADER
Specify public header files in a FRAMEWORK shared library target.
Shared library targets marked with the FRAMEWORK property generate
frameworks on macOS, iOS and normal shared libraries on other
platforms. This property may be set to a list of header files to be
placed in the Headers directory inside the framework folder. On
non-Apple platforms these headers may be installed using the
PUBLIC_HEADER option to the install(TARGETS) command.
So the PUBLIC_HEADER attribute is only about installing the public headers. To do so in qmake, you need to use INSTALLS in testlib.pro, for instance:
unix {
target.path = /usr/local/lib
headers.path = /usr/local/include
}
!isEmpty(target.path) {
INSTALLS += target
}
headers.files = $$HEADERS
!isEmpty(headers.path) {
INSTALLS += headers
}
Related
I am trying to make a native C++ code in Android Studio and with CMake. My C++ code uses precompiled static library (.a file).
I included its header .h in my C++ code. I also linked the location of the .h and the .a files in my CMakeList.txt as below:
include_directories(".h file location")
Then:
add_library(lib_fastcv STATIC IMPORTED)
set_target_properties(lib_fastcv PROPERTIES IMPORTED_LOCATION
".a file location")
And at the end:
target_link_libraries (...lib_fastcv....)
However, as soon as I use any function from the .a static library it complains that it cannot recognize the function which means the static library is not linked correctly to my C++ code.
Does anyone know what else I need to do?
Should I also edit my build.gradle to include information about the library file?
this is my solution:
So, first of all, CMake can be weird to use at the beginning.
1- native-lib1 is the output product of CMake. Later java will only see .so of this
add_library( # This is out .so product (libnative-lib1.so)
native-lib1
# Sets the library as a shared library.
SHARED
# These are the input .cpp source files
native-lib.cpp
SRC2.cpp
Any other cpp/c source file you want to compile
)
2- Any library that you included in your souse file, its .h needs to be addressed here:
target_include_directories(# This is out .so product (libnative-lib1.so)
native-lib1 PRIVATE
${CMAKE_SOURCE_DIR}/include)
3- Any actual library you used, its atual .a or .cpp should be addrressed to CMake this way:
target_link_libraries(
# This is out .so product (libnative-lib1.so)
native-lib1
#These are any extra library files that I need for building my source .cpp files to final .so product
${CMAKE_SOURCE_DIR}/lib/libfastcv.a
# Links the target library to the log library
# included in the NDK.
${log-lib})
Then build.gradle it should be mentioned what abi formats we want it to make to make sure you pre built .a files are compatible.
flavorDimensions "version"
productFlavors {
create("arm7") {
ndk.abiFilters("armeabi-v7a")
}
Finally, as a note, the below command did not work to filter abis and above command work, even though they look similar in logic and form to me:
cmake {
cppFlags "-std=c++11"
abiFilters 'armeabi-v7a'
}
}
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
// This is not working to eliminate
abiFilters 'armeabi-v7a'
}
}
I have seen plenty of answers around about the issue but non of them are based on my case and "ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES" does not work either.
I am developing a framework that is an implementation of a C library. The library is OpenHome and after compiling it and creating the fat libs I have a folder with all the .a and the headers.
Since It has a folder for "Debug" and "Release", I copy this 2 folders in the root of my project, I import the .a files into my "Link binary with libraries" and, in the "Build Settings" of my target, I set the "Header Search Path" with the location of the headers folder.
for importing all the Headers I need to implement I use a c++ class called "MyHeaders.hpp & MyHeader.cpp", I make the the .hpp pubic and I import it like that in my MyFramework.h (Umbrella file):
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"
#import "MyHeaders.hpp"
#pragma clang diagnostic pop
MyHeaders.hpp:
#ifndef MyHeaders_hpp
#define MyHeaders_hpp
#include <stdio.h>
/*** CP -PROXIES- ***/
/** Header That Includes all the CP Related Headers **/
#include "OpenHome/Net/C/CpStack.h"
/** CP Services **/
/*UPnP*/
#include "OpenHome/Net/C/CpUpnpOrgConnectionManager1.h"
#include "OpenHome/Net/C/CpUpnpOrgRenderingControl1.h"
#include "OpenHome/Net/C/CpUpnpOrgAVTransport1.h"
#endif /* MyHeaders_hpp */
The error come from each include. I have replaced wit import but it also doesn't work.
It is worth mentioning that this project configuration was the one I used in the project (Single View Application) I started implementing and testing. The only difference was the existence of the bridging header.
Any Ideas?
Well after a researching i found the issue and the solution
When I am #include "OpenHome/Net/C/CpUpnpOrgConnectionManager1.h" this header is either not public or contains non public headers.
The solution for importing this headers in a Swift based framework is to to create a Module.
I have created a folder "MyModuleFolder" in my target folder and I have created a "module.modulemap" file inside that looks like
module OHNet[system]{
header "MyHeaders.hpp"
export *
}
After that, in "Build Settings" -> "Swift Compiler - Search Paths " -> "Import Paths" i have added the location for "MyModuleFolder".
Finally,
import OHNet
in each swift file
I hope it helps.
I have two projects in qtcreator: BiosPatcher - the code & BiosPatcherTest - googletest unit tests for BiosPatcher code. Advice from here:
multiple main declaration in qtcreator project which uses googletest
How to import sources from one project in another and connect builded project as library in qtcreator to another qtcreator project?
BiosPatcher project has:
BiosPatcher\src\bios\Bios.{cpp, hpp} class
and in BiosPatcherTest i have test:
#include "src/bios/Bios.hpp" //not works
...
TEST(BiosTest, testReadMethodReadsFromBiosIO) {
MockBiosIO mockBiosIO;
EXPECT_CALL(mockBiosIO, readAsBytes())
.Times(AtLeast(1));
MockReentrantLock mockReentrantLock;
MockBiosVector mockBiosVector;
MockPatch mockPatch;
MockLog mockLog;
Bios bios;
bios.setBiosIO(&mockBiosIO);
bios.setLock(&mockReentrantLock);
bios.setBiosBytesVector(&mockBiosVector);
bios.setLog(&mockLog);
bios.setPatch(&mockPatch);
bios.read();
}
First, you need create BiosPatcher C++ shared library project.
After, you need just add BIOSPATCHER_LIBRARY to class declaration
this
class BiosPatcher
{
need to be replaced by this
class BIOSPATCHERSHARED_EXPORT BiosPatcher
{
and after it just compile your library and include same headers in your second project, and add to linking generated .lib file.
DEPENDPATH''= . ../BiosPatcher
INCLUDEPATH ''= ../BiosPatcher
LIBS''= -L../BiosPatcher/debug -lBiosPatcher
When I created library project in my Qt Creator with name BiosPatcher, IDE automaticly created biospatcher_global.h file, that containts BIOSPATCHERSHARED_EXPORT defination. That file need to be included in all library headers.
You see, if defined BIOSPATCHER_LIBRARY then BIOSPATCHERSHARED_EXPORT will be defined as Q_DECL_EXPORT, else Q_DECL_IMPORT. BIOSPATCHER_LIBRARY is defined in library .pro file, that means if you include those headers from another project, class will be imported from compiled library.
#ifndef BIOSPATCHER_GLOBAL_H
#define BIOSPATCHER_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(BIOSPATCHER_LIBRARY)
# define BIOSPATCHERSHARED_EXPORT Q_DECL_EXPORT
#else
# define BIOSPATCHERSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // BIOSPATCHER_GLOBAL_H
Also read this: https://wiki.qt.io/How_to_create_a_library_with_Qt_and_use_it_in_an_application
I have to use zlib (ver 1.2.8, already compiled one from the official source) inside my DLL. I'm using static linking, but unable to use functions or objects from zlib - gives me "ะก2065 undeclared identifier". I'm using Qt 5.4.1 and compiler fom MSVS2013. This is my pro file:
QT -= gui
TARGET = ZLIB
TEMPLATE = lib
DEFINES += ZLIB_LIBRARY
ZLIBDIR += "Y:/Git/Modules/TestZLIB/Libs" #It contains zlib's dll, lib and def
INCLUDEPATH += $${ZLIBDIR}
win32 {
LIBS += -L$${ZLIBDIR} -lzdll
}
SOURCES += \
Library.cpp \
myzlib.cpp
HEADERS +=\
myzlib.h \
myzlib_global.h \
zconf.h \
zlib.h
unix {
target.path = /usr/lib
INSTALLS += target
}
UPDATE:
Path to .lib, .dll, .def is Y:/Git/Modules/TestZLIB/Libs.
zlib.h and zconf.h is in project folder and also I've tried to copy them to Y:/Git/Modules/TestZLIB/Libs and connect them to the project from there, but it doesn't work for me.
Other source files are in Y:/Git/Modules/TestZLIB/TestZLIB.
Y:/Git/Modules/TestZLIB/TestZLIB - this is a project folder (sources, .pro file etc.).
Y:/Git/Modules/TestZLIB/Libs - this is folder with zlib's files.
What can I do to make it works?
Thank you!
When asking questions, it's common courtesy to set them to be unambiguous. Even if your project is really named zlib, you should never ask a question with such a project name, since it's impossible to answer any questions about it without having to resolve ambiguity at every step. It'll also possibly confound the error messages you get. From now on, when talking of zlib, I'm talking of the compression library, not your project.
If the only compiler error is about unknown identifiers, then your problem isn't with the .pro file. The compiler simply doesn't see zlib's declarations. The following are possible mistakes:
You're not including zlib headers in your own files.
Your include guards clash with those of zlib and effectively hide the zlib header contents.
If you wrote your own zlib.h, it may be hiding zlib's own headers. You must rename it to something else if so.
If zlib.h and zconf.h come from zlib, they don't belong in your .pro file.
Make sure that neither is the case.
I wasn't sure what to search for for this one. So excuse me if this is simple. But let me outline the scenario and see what answers are out there.
Let's say I have a library which defines a structure like this:
struct Example {
int a;
#if B_ENABLED
int b;
#endif
};
This header gets installed as a part of the library's installation as a whole. My question here is that if my library defines B_ENABLED it will have a structure with these two variables included. However if my application does not define this as well. Then it will interpret the header as defining a struct with only one member.
Is the best way to handle this just to generate some kind of "options" header which would include all of the #defines that were specified in the library build?
My library builds with CMAKE. So a CMAKE solution for this is extra credit =D.
Solution #1 (configure + install)
Include config.hpp file in your header file(i.e. foo.hpp):
#ifndef FOO_HPP_
#define FOO_HPP_
#include "config.hpp" // FOO_DEBUG
class Foo {
public:
int result() const;
private:
int a_;
#ifdef FOO_DEBUG
int b_;
#endif // FOO_DEBUG
};
#endif // FOO_HPP_
config.hpp is output of configure_file command:
configure_file(config.hpp.in "${PROJECT_BINARY_DIR}/config/config.hpp")
include_directories("${PROJECT_BINARY_DIR}/config")
install(FILES Foo.hpp "${PROJECT_BINARY_DIR}/config/config.hpp" DESTINATION include)
input file config.hpp.in use special cmakedefine directive:
#ifndef CONFIG_HPP_
#define CONFIG_HPP_
#cmakedefine FOO_DEBUG
#endif // CONFIG_HPP_
Note that when you use installed library in other project:
you still need to specify include directories for library
if your library have dependencies you need to link them manually
you can't have 2 config files (Debug/Release)
Solution #2 (export/import target, recommended)
install(EXPORT ...) command can hold all information about using library
(aka usage requirements: including definitions, linked library, configuration etc):
add_library(Foo Foo.cpp Foo.hpp)
# Target which used Foo will be compiled with this definitions
target_compile_definitions(Foo PUBLIC $<$<CONFIG:Release>:FOO_DEBUG=0>)
target_compile_definitions(Foo PUBLIC $<$<CONFIG:Debug>:FOO_DEBUG=1>)
# This directory will be used as include
target_include_directories(Foo INTERFACE "${CMAKE_INSTALL_PREFIX}/include")
# This library will be linked
target_link_libraries(Foo PUBLIC pthread)
# Regular install
install(FILES Foo.hpp DESTINATION include)
# Install with export set
install(TARGETS Foo DESTINATION lib EXPORT FooTargets)
install(EXPORT FooTargets DESTINATION lib/cmake/Foo)
Installing such project will produce files (CMAKE_DEBUG_POSTFIX is d):
include/Foo.hpp
lib/libFoo.a
lib/libFood.a
lib/cmake/Foo/FooTargets-debug.cmake
lib/cmake/Foo/FooTargets-release.cmake
lib/cmake/Foo/FooTargets.cmake
Include FooTargets.cmake file to import installed library to project. For example using find_package command (need config, see configure_package_config_file):
add_executable(prog main.cpp)
find_package(Foo REQUIRED) # import Foo
target_link_libraries(prog Foo)
Note that:
path to include/Foo.hpp automatically added to compiler options
dependend library pthread is automatically added to prog linker option
definition FOO_DEBUG=0 added to Release build type
definition FOO_DEBUG=1 added to Debug build type
Rationale
So excuse me if this is simple
It is not (:
The root of the problem is ODR (C++ Standard 2011, 3.2 [basic.def.ord], p.3):
Every program shall contain exactly one definition of every non-inline function
or variable that is odr-used in that program; no diagnostic required. The
definition can appear explicitly in the program, it can be found in the
standard or a user-defined library
IMHO good general solution still not exists. Using CMake with imported configuration
can partially helps a little bit, but in some cases you still will get linker errors
(for example if you use library compiled with gcc, which linked to libstdcxx by default,
and try to link it to project with clang compiler, which linked to libcxx).
Some of this problems (not all, still) can be solved using toolchain files.
See examples.
Related
CMake tutorial
Exporting/importing targets
Modern CMake with Qt and Boost