Swift Package Manager C-interop: Non-system libraries - c

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".

Related

How do I add .a files to Cmake [duplicate]

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/

Linker error in Meson & Ninja when trying to add custom dependency

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

Importing Modules — customized Modules in C

I am currently learning the C programming language and I'm having some issues with importing modules I created.
I created a small module to read with fgets and flush the buffer from stdin perfectly and I don't want to keep writing the code every single time. I just want to import this small module like I used to do in Python. I didn't knew how because I'm not using an IDE. I'm just compiling with gcc in terminal and using a text editor. I tried to search with Google,but in vain.
You should create a header for your module that declares the functions in the module – and any other information that a consumer of the module needs. You might call that header weekly.h, a pun on your name, but you can choose any name you like within reason.
You should create a library (shared or static — that's up to you) that contains the functions (and any global variables, if you're so gauche as to have any) defined by your module. You might call it libweekly.so or libweekly.a — or using the extensions appropriate to your machine (.dylib and .a on macOS, for example). The source files might or might not be weekly.c — if there's more than one function, you'll probably have multiple source files, so they won't all be weekly.c. You should put this code (the header and the source files and their makefile) into a separate source directory.
You should install the header(s) and the library in a well-known location (e.g. $HOME/include for headers and $HOME/lib for the library — or maybe in the corresponding directories under /usr/local), and then ensure that the right options are used when compiling (-I$HOME/include for the headers) or linking (-L$HOME/lib and -lweekly).
Your source code using the module would contain:
#include "weekly.h"
and your code would be available. With shared libraries in $HOME/lib, you would have to ensure that the runtime system knows where to find the library. If you install it in /usr/local, that is done for you already. If you install it in $HOME/lib, you have to investigate things like /etc/ld.so.conf or the LD_LIBRARY_PATH or DYLIB_LIBRARY_PATH environment variables, etc.
You need to create a header file (.h) with your function declarations types and extern variables. Then in the program where you want to use those functions include this .h file and and add the compiled .o file (with your functions) to the object file list. And you are done.

How to get right values from requirements in Conan recipe to be used as CMake parameters?

I'm writing recipe for creating package for rabbitmq-c library. When ENABLE_SSL_SUPPORT option in its CMake script is checked it requires OpenSSL library for its build.
As it shown on provided screen paths to Debug and Release versions of libeay.lib and ssleay.lib files are required.
In my conanfile.py for rabbitmq-c library I have the following code which describes the dependency.
def requirements(self):
if self.options.ssl_support:
self.requires("OpenSSL/1.0.2l#bobeff/stable")
How to get right values from required OpenSSL package to set them in CMake configure options for RabbitMQ-C recipe?
The package OpenSSL/1.0.2l#bobeff/stable can be build with different settings and options. How to choose which to be used when I'm building RabbitMQ-C? For example how to choose whether static or dynamic version of OpenSSL to be used for linking with RabbitMQ-C dll files?
You have full access to the dependencies model inside your build() method, so you can access:
def build(self):
print(self.deps_cpp_info["OpenSSL"].rootpath)
print(self.deps_cpp_info["OpenSSL"].include_paths)
print(self.deps_cpp_info["OpenSSL"].lib_paths)
print(self.deps_cpp_info["OpenSSL"].bin_paths)
print(self.deps_cpp_info["OpenSSL"].libs)
print(self.deps_cpp_info["OpenSSL"].defines)
print(self.deps_cpp_info["OpenSSL"].cflags)
print(self.deps_cpp_info["OpenSSL"].cppflags)
print(self.deps_cpp_info["OpenSSL"].sharedlinkflags)
print(self.deps_cpp_info["OpenSSL"].exelinkflags)
Also if you want to access the aggregated values (for all the dependencies/requirements), you can do:
def build(self):
print(self.deps_cpp_info.include_paths)
print(self.deps_cpp_info.lib_paths)
...
So, given those values, you can pass them to your build system, in the case of CMake you could do something like:
def build(self):
cmake = CMake(self)
# Assuming there is only 1 include path, otherwise, we could join it
cmake.definitions["SSL_INCLUDE_PATH"] = self.deps_cpp_info["OpenSSL"].include_paths[0]
That will be translated to a cmake command including the -DSSL_INCLUDE_PATH=<path to openssl include> flag.
If you go for multi-configuration packages, you can check (http://docs.conan.io/en/latest/packaging/package_info.html#multi-configuration-packages). They will define debug, release configs, that you can also later use in your model:
def build(self):
# besides the above values, that will contain data for both configs
# you can access information specific for each configuration
print(self.deps_cpp_info["OpenSSL"].debug.rootpath)
print(self.deps_cpp_info["OpenSSL"].debug.include_paths)
...
print(self.deps_cpp_info["OpenSSL"].release.rootpath)
print(self.deps_cpp_info["OpenSSL"].release.include_paths)
...

How to make OCamlbuild compile and link a C file into an OCaml project?

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).

Resources