I am trying to add source-based library to my project using Meson. But when I try doing that, I get object file linking errors.
I tried adding custom dependency to project executable, but of course, it says it is undefined(srclibdep in code ahead), since it is defined after project executable. But if I define before Project executable, then I can't link.
This is my ./meson.build
project('ProjectName', 'cpp', version: '0.1', default_options: ['warning_level=3', 'cpp_std=c++14'])
srclibinc = include_directories('SourceLibraryName')
cpp = meson.get_compiler('cpp')
add_languages('cpp')
proj = executable('ProjectName', 'main.cpp', install: true, include_directories: srclibinc)
srclibdep = declare_dependency(include_directories: srclibinc, link_with: proj)
And ./SourceLibraryName/meson.build
files = run_command('files.sh').stdout().strip().split('\n')
foreach f: files
install_headers(f)
endforeach
srclib = shared_library('SourceLibrary', files, install: true)
pkg_mod = import('pkgconfig')
pkg_mod.generate(libraries: srclib, version: '0.1', name: 'libsrc', description: 'Source-based library.')
I am getting hundreds of linking errors saying that x::Y reference doesn't exist, but compiler compiled the code as if dependency is already there.
I think it should be
// make srclib available to code below:
subdir('SourceLibraryName')
// create dependency object with library to link against:
srclibdep = declare_dependency(link_with: srclib)
// add this object to dependencies:
proj = executable('ProjectName', 'main.cpp', install: true, include_directories: srclibinc, dependencies : srclibdep)
PS:
Not related to the matter, but just noticed:
you don't have to generate pkg-config file if you use shared library only in your project
it's good practice to add version to shared library, especially if it will be shared with other projects:
shared_library('SourceLibrary', files, install: true, version : meson.project_version())
meson.project_version() you can use for pkg-config file as well, so you won't forget update it in all places
you don't install any headers for library, so other projects won't find API that your library provides
Related
How to get CMake to link an executable to an external shared library that is not build within the same CMake project?
Just doing target_link_libraries(GLBall ${CMAKE_BINARY_DIR}/res/mylib.so) gives the error
make[2]: *** No rule to make target `res/mylib.so', needed by `GLBall'. Stop.
make[1]: *** [CMakeFiles/GLBall.dir/all] Error 2
make: *** [all] Error 2
(GLBall is the executable)
after I copied the library into the binary dir bin/res.
I tried using find_library(RESULT mylib.so PATHS ${CMAKE_BINARY_DIR}/res)
Which fails with RESULT-NOTFOUND.
arrowdodger's answer is correct and preferred on many occasions. I would simply like to add an alternative to his answer:
You could add an "imported" library target, instead of a link-directory. Something like:
# Your-external "mylib", add GLOBAL if the imported library is located in directories above the current.
add_library( mylib SHARED IMPORTED )
# You can define two import-locations: one for debug and one for release.
set_target_properties( mylib PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/res/mylib.so )
And then link as if this library was built by your project:
TARGET_LINK_LIBRARIES(GLBall mylib)
Such an approach would give you a little more flexibility: Take a look at the add_library( IMPORTED) command and the many target-properties related to imported libraries.
I do not know if this will solve your problem with "updated versions of libs".
Set libraries search path first:
link_directories(${CMAKE_BINARY_DIR}/res)
And then just do
target_link_libraries(GLBall mylib)
I assume you want to link to a library called foo, its filename is usually something link foo.dll or libfoo.so.
1. Find the library
You have to find the library. This is a good idea, even if you know the path to your library. CMake will error out if the library vanished or got a new name. This helps to spot error early and to make it clear to the user (may yourself) what causes a problem.
To find a library foo and store the path in FOO_LIB use
find_library(FOO_LIB foo)
CMake will figure out itself how the actual file name is. It checks the usual places like /usr/lib, /usr/lib64 and the paths in PATH.
You already know the location of your library. Add it to the CMAKE_PREFIX_PATH when you call CMake, then CMake will look for your library in the passed paths, too.
Sometimes you need to add hints or path suffixes, see the documentation for details:
https://cmake.org/cmake/help/latest/command/find_library.html
2. Link the library
From 1. you have the full library name in FOO_LIB. You use this to link the library to your target GLBall as in
target_link_libraries(GLBall PRIVATE "${FOO_LIB}")
You should add PRIVATE, PUBLIC, or INTERFACE after the target, cf. the documentation:
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
If you don't add one of these visibility specifiers, it will either behave like PRIVATE or PUBLIC, depending on the CMake version and the policies set.
3. Add includes (This step might be not mandatory.)
If you also want to include header files, use find_path similar to find_library and search for a header file. Then add the include directory with target_include_directories similar to target_link_libraries.
Documentation:
https://cmake.org/cmake/help/latest/command/find_path.html
and
https://cmake.org/cmake/help/latest/command/target_include_directories.html
If available for the external software, you can replace find_library and find_path by find_package.
Let's say you have an executable like:
add_executable(GLBall GLBall.cpp)
If the external library has headers, give the path to its include folder:
target_include_directories(GLBall PUBLIC "/path/to/include")
Add the library directory path:
target_link_directories(GLBall PUBLIC "/path/to/lib/directory")
Finally, link the library name
target_link_libraries(GLBall mylib)
Note that the prefix and extension of the library file are removed:
libmylib.a ➜ mylib
mylib.so ➜ mylib
One more alternative, in the case you are working with the Appstore, need "Entitlements" and as such need to link with an Apple-Framework.
For Entitlements to work (e.g. GameCenter) you need to have a "Link Binary with Libraries"-buildstep and then link with "GameKit.framework". CMake "injects" the libraries on a "low level" into the commandline, hence Xcode doesn't really know about it, and as such you will not get GameKit enabled in the Capabilities screen.
One way to use CMake and have a "Link with Binaries"-buildstep is to generate the xcodeproj with CMake, and then use 'sed' to 'search & replace' and add the GameKit in the way XCode likes it...
The script looks like this (for Xcode 6.3.1).
s#\/\* Begin PBXBuildFile section \*\/#\/\* Begin PBXBuildFile section \*\/\
26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks \*\/ = {isa = PBXBuildFile; fileRef = 26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/; };#g
s#\/\* Begin PBXFileReference section \*\/#\/\* Begin PBXFileReference section \*\/\
26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameKit.framework; path = System\/Library\/Frameworks\/GameKit.framework; sourceTree = SDKROOT; };#g
s#\/\* End PBXFileReference section \*\/#\/\* End PBXFileReference section \*\/\
\
\/\* Begin PBXFrameworksBuildPhase section \*\/\
26B12A9F1C10543B00A9A2BA \/\* Frameworks \*\/ = {\
isa = PBXFrameworksBuildPhase;\
buildActionMask = 2147483647;\
files = (\
26B12AA11C10544700A9A2BA \/\* GameKit.framework in Frameworks xxx\*\/,\
);\
runOnlyForDeploymentPostprocessing = 0;\
};\
\/\* End PBXFrameworksBuildPhase section \*\/\
#g
s#\/\* CMake PostBuild Rules \*\/,#\/\* CMake PostBuild Rules \*\/,\
26B12A9F1C10543B00A9A2BA \/\* Frameworks xxx\*\/,#g
s#\/\* Products \*\/,#\/\* Products \*\/,\
26B12AA01C10544700A9A2BA \/\* GameKit.framework xxx\*\/,#g
save this to "gamecenter.sed" and then "apply" it like this ( it changes your xcodeproj! )
sed -i.pbxprojbak -f gamecenter.sed myproject.xcodeproj/project.pbxproj
You might have to change the script-commands to fit your need.
Warning: it's likely to break with different Xcode-version as the project-format could change, the (hardcoded) unique number might not really by unique - and generally the solutions by other people are better - so unless you need to Support the Appstore + Entitlements (and automated builds), don't do this.
This is a CMake bug, see http://cmake.org/Bug/view.php?id=14185 and http://gitlab.kitware.com/cmake/cmake/issues/14185
It has been a long time since the question was posted but I am leaving this one just for reference.
I have a blog post describing step-by-step almost what you (or anyone else) were trying to do.
Please check here: https://michae9.wordpress.com/2022/09/01/shared-lib-to-be-used-by-client-programs-with-cmake/
How can I use the Swift Package Manager to include C code (in my case, a single .c file and a header file) without requiring the user to install my C library into /usr/local/lib?
I had thought to create a Package in a subdirectory of my main package containing the header + lib, and use relative paths, and finally build with swift build -Xlinker ./relative/path/to/mylib, however I'm not having any success resolving the dependency since it's expected to be a standalone git repository. Error message is:
error: failed to clone; fatal: repository '/absolute/path/to/mylib' does not exist
Moreover it's not clear to me whether using the -Xlinker flag is the correct approach.
I can't use a bridging header with a pure SwiftPM approach and installing my library system-wide seems overkill as well as not very portable.
Any ideas?
I have done that in this project on github. It replaces pthread_once_t by wrapping it in C and re-exposing it to swift. It was done as a fun exercise in getting around what Swift tries to limit you into since pthread_once_t and dispatch_once are not available directly.
Here is a trimmed down version the Package.swift file:
// swift-tools-version:4.0
import PackageDescription
let package = Package(
name: "Once",
products: [
.library(
name: "Once",
targets: ["OnceC", "Once"]),
],
dependencies: [
],
targets: [
.target(
name: "OnceC",
dependencies: [],
path: "Sources/OnceC"),
.target(
name: "Once",
dependencies: ["OnceC"],
path: "Sources/Swift"),
.testTarget(
name: "OnceTests",
dependencies: ["Once"]),
]
)
You can easily replace the product library with an executable. The main part is that the product's targets needs to contain both the C and Swift targets needed to build.
Then in your targets section make the swift target lists the C target as a dependency.
You can learn more about the required layout for C targets in the SwiftPM Usage.md here
C language targets
The C language targets are similar to Swift targets except that the C language
libraries should contain a directory named include to hold the public headers.
To allow a Swift target to import a C language target, add a target
dependency in the manifest file. Swift Package Manager will
automatically generate a modulemap for each C language library target for these
3 cases:
If include/Foo/Foo.h exists and Foo is the only directory under the
include directory then include/Foo/Foo.h becomes the umbrella header.
If include/Foo.h exists and include contains no other subdirectory then
include/Foo.h becomes the umbrella header.
Otherwise if the include directory only contains header files and no other
subdirectory, it becomes the umbrella directory.
In case of complicated include layouts, a custom module.modulemap can be
provided inside include. SwiftPM will error out if it can not generate
a modulemap w.r.t the above rules.
For executable targets, only one valid C language main file is allowed i.e. it
is invalid to have main.c and main.cpp in the same target.
The only other important thing is how you actually do your #import in the C code once it is compiled as a compatible module. If you use the import/Foo/Foo.h organization you need to use #include <Foo/Foo.h> and if you do import/Foo.h you can use #import "Foo.h".
I'm trying to build an OCaml binary main.native from main.ml that also relies on a single C file custom.c to implement a new primitive. This C file needs to be compiled and linked. Is there a way to do this just with a _tags file? The obvious problem is that OCamlbuild does not detect the dependency when scanning main.ml and hence needs to be told explicitly about the dependency.
ocamlbuild main.native
OCamlbuild knows a rule to compile a *.c file to a *.o file but I don't know how to add the dependency.
There are a number of resources out there.
The first thing is that you need to tag the main.native as to create a dependency on the c-stubs and link accordingly. (By the way, this assumes the c-library is called cstub, but it can be anything you'd like).
_tags :
<*.{byte,native}> : use_cstub
<**/*.cm{x,}a> : use_cstub
Then, in myocamlbuild.ml create a dependency of a c-library to the things tagged,
dep ["link";"ocaml";"use_cstub"] ["libcstub.a"]
OCamlbuild has rules for creating library files (*.so and *.a), but you would need to add a listing of the files to be built against in a .clib file,
cstub.clib :
cobjfile1.o
cobjfile2.o
...
Any header files also need to be copied over from the main directory to the _build/ directory. This is done by specifying they are a dependency on compilation in c (in myocamlbuild.ml, where headers is a list of strings naming the header files in the project.
dep ["c"; "compile"] headers;
and finally, adding flags to when we link the project with the c-stub library (also in myocamlbuild.ml),
flag ["link";"ocaml";"use_cstub"] (S[A"-dllib";A"-lcstub";A"-cclib";A"-lcstub"]);
I have accepted the answer above but would like to document the solution. As mentioned in a comment, I have created a new parametrized tag linkdep() using myocamlbuild.ml:
open Ocamlbuild_plugin;;
dispatch
( function
| After_rules -> pdep ["link"] "linkdep" (fun param -> [param])
| _ -> ()
)
The newly created tag is used in _tags to add a link dependency:
<*.ml>: annot
<*.byte>: linkdep(custom_unix_stubs.o),custom
<*.native>: linkdep(custom_unix_stubs.o)
This relies on built-in rules to compile a C file to an object file. However, this would still miss dependencies on header files (which I don't have).
I am trying my hand at autotools. I have the following project hierarchy:
project/src
project/src/utilities
project/src/utilities/util.c
project/src/utilities/util.h
project/src/sharedObject
project/src/sharedObject/sharedObject.c
project/src/sharedObject/sharedObject.h
project/src/sharedObject/thing.c
project/src/executable
project/src/executable/exec.c
project/src/executable/exec.h
project/src/executable/thing1.c
project/src/executable/thing2.c
"executable" and "sharedObject.so" both depend on "util.o" and "util.h". I have seen examples of creating convenience libraries but I am not sure how to specify them in the "Makefile.am" files in the other two sub-projects. How are these kinds of inter-project dependencies defined?
Both "executable" and "sharedObject.so" will be installed. The "util.o" and "util.h" files will only be used in the build process.
Thank you
In the utilities/Makefile.am:
noinst_LTLIBRARIES = libutil.la
libutil_la_SOURCES = util.h util.c
In executable/Makefile.am, the use of the library should use the LDADD primary, e.g.,
bin_PROGRAMS = exec
exec_SOURCES = exec.h exec.c thing.h thing.c
exec_LDADD = ../utilities/libutil.la
In sharedObject/Makefile.am, use the LIBADD primary:
lib_LTLIBRARIES = sharedObject.la
sharedObject_la_SOURCES = sharedObject.h sharedObject.c thing.c
sharedObject_la_LIBADD = ../utilities/libutil.la
If you actually want a sharedObject.so that is to be dynamically loaded, you also need:
sharedObject_la_LDFLAGS = -module
Otherwise, the target should be called libsharedObject.
The top level Makefile.am should order SUBDIRS so that the dependency is built first:
SUBDIRS = utilities executable sharedObject
I'm trying to get SCons to make a shared library. One of the items going into the .so is a .a static lib.
I have a line like:
env_2.SharedLibrary('libstuff.so', \
Split("""stuff.cxx mylib/libMine.a""")
And upon running it, I get this error:
scons: *** Source file: mylib/libMine.a \
is static and is not compatible with shared target: libstuff.so
However, I know that a shared library can be made from the .a via a command like:
g++ -m32 -shared -o libstuff.so stuff.o mylib/libMine.a
Any ideas on getting this to work or any workarounds would be greatly appreciated.
Related question: How do I get scons to put an additional string -shared on the LINK command line for the Program() call? If I could do this, I think it would meet my needs.
Try to set env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME']=1 in your SConstruct.
This problem is not specific to scons. To build a shared library, you'll need objects that are compiled with position independent code (-fPIC). Your best bet is to make the shared library out of the source files compiled with the right options.
In SCons, you can define a list of targets that's used to build both libMine.a and libShared.so.
Update: for your second question, the SharedLibrary builder might do what you need:
SharedLibrary('foo', ['f1.c', 'f2.c', 'f3.c'])
If not, LINKFLAGS sets the flags passed to a link command.
I've the same problem under cygwin. I passed '-fPIC' options to gcc when building the objects and got the following warning:
warning: -fPIC ignored for target (all code is position independent)
I also passed '-shared' to the link command. And I finally got the error
"***.lib is static and is not compatible with shared target: myso.dll"
It seems scons doesn't allow to create so directly from obj or lib files, and one can either create the so from a list of sources files (using SharedLibrary()) or source file + 'LIBS' option like dummytaurus says. I'm curious about that.
env_2.SharedLibrary('libstuff.so', Split("""stuff.cxx"""), LIBS='libMine.a', LIBPATH='mylib')
This should work.
The problem is in the function SharedFlagChecker (Default.py), which checks only for an internal "shared" flag. The SCons documentation leads you to believe that it keeps the distinction between shared objects and static objects via the suffix (SHOBJSUFFIX), but not so. The fix is easy. In the file scons-local.../SCons/Default.py find the SharedFlagCheckerand edit:
def SharedFlagChecker(source, target, env):
same = env.subst('$STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME')
if same == '0' or same == '' or same == 'False':
for src in source:
try:
shared = src.attributes.shared
except AttributeError:
# Replace this line: shared = None
shared = env.Dictionary()['SHOBJSUFFIX'] == src.suffix
if not shared:
raise SCons.Errors.UserError("Source file: ...")
Now objects made via the SharedObject builder will be linkable in a shared library.
The solution to resolve this issue have been used a 'SharedObject' as the following:
ASN1CObjectFile = envCProcesses.SharedObject(target = "OTCMsg.os",
source = "OTCMsg.c",
CFLAGS = envCProcesses["CFLAGS"] + ["-Wno-unused"])
and:
OTCLibSharedLibrary = envCProcesses.SharedLibrary(env["OTC_LIBPATH"] + "/libOTCLib" + env["SHLIBSUFFIX"],
ASN1CObjectFile +
[ASN1AllocatorSource,
"OTCLib.c"],
LIBPREFIX = "")
Best regards,