Cython: Link to library and call function - c

I am trying to write a Cython wrapper for a C Library. I have read through the docs carefully but I must be missing something since I cannot get the following simple Cython code to work.
I created a shared library from the following:
mathlib.c
#include "mathlib.h"
int add_one(int x){
return x + 1;
}
mathlib.h
extern int add_one(int x);
Then I created the library like so:
gcc -c mathlib.c
gcc -shared -o libmathlib.so mathlib.o -lm
My Cython files are mathlib.pyx, cmathlib.pyd, and setup.py
cymathlib.pyx
from mathlib cimport add_one
def add_one_c(int x):
print add_one(x)
return x
mathlib.pyd
cdef extern from "mathlib.h":
int add_one(int x)
setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
setup(
ext_modules = cythonize([Extension("cymathlib", ["cymathlib.pyx"])], libraries ["mathlib"])
)
The module cymathlib.so is created but when I attempt to import it in Python I get the following error: ImportError: dlopen(./cymathlib.so, 2): Symbol not found: _add_one Referenced from: ./cymathlib.so Expected in: flat namespace in ./cymathlib.so"

It looks like something went wrong with your Extension specification, which should be something like:
ext_modules = cythonize([Extension("cymathlib",
["cymathlib.pyx"],
libraries=["mathlib"],
library_dirs=["/place/path/to/libmathlib.so/here"]
)])
To be able to use the module, it must be able to find libmathlib.so at run time, since it will look in that file for the actual implementation of add_one. Apart from copying the file to /usr/lib or /usr/local/lib (and running ldconfig again), you can also set an environment variable to make sure the library can be found:
export LD_LIBRARY_PATH=/place/full/path/to/libmathlib.so/here
It's also possible to add the C code into the python module you're creating (so no libmathlib.so will need to be compiled or used anymore). You just need to add the mathlib.c file to the list of cython sources:
ext_modules = cythonize([Extension("cymathlib",
["cymathlib.pyx","mathlib.c"]
)])

Related

Utilising Cython with Pyinstaller

I am trying to build a program in python using Cython and PyInstaller. Before starting, I built this test program. However, the two modules aren't working together at all. I have tried them both separately, and they work.
I looked at this question but that doesn't work.
I also tried using cython to compile it to c and then use gcc, but that doesn't work either (I added the python.h file, and gcc just aborts after complaining that the keyword doesn't exist)
#hello.pyx
import pygame
def say_hello_to(name):
print("Hello %s!" % name)
#hello.py
from hello import say_hello_to
say_hello_to('Me')
#setup.py
from setuptools import setup
from Cython.Build import cythonize
setup(
name='Hello world app',
ext_modules=cythonize("hello.pyx"),
zip_safe=False,
)
All of this works in CPython, with :
python3 setup.py build_ext --inplace
Then
python3 hello.py
But PyInstaller keeps complaining it can't find the module hello. I tried add-data and add-binaries, as well as editing the spec file, and it still doesn't work, saying:
invalid add_data_or_binary value: 'home:Achyut-BK:hello.cpython-38-x86_64-linux-gnu.so'

How integrate gnatmake/gnatbind/gnatlink in CMake files for C/Ada code?

I wrote a code in a few languages (C, C++, Fortran77, Fortran90) and I can compile it without any sort of problem by using CMake. It works out perfectly.
Now, I would like to add in the main(), which is written in C, some Ada function and I want to compile it by CMake. Given that I am not able to link my Ada function to the main one by using CMake, I get
main.c:(.text.startup+0x16a): undefined reference to adainit
main.c:(.text.startup+0x179): undefined reference to adafunction
main.c:(.text.startup+0x190): undefined reference to adafinal
I did another simplified test by using the main function (written in C) calling the only Ada function, which I coded, and I compiled it by using
gcc -c main.c
gnatmake -c lib_ada.ali
gnatbind -n lib_ada.ali
gnatlink lib_ada.ali main.o -o exe
and it works out. Do you know how I can integrate this approach in a CMakeList.txt?
Note: I think (maybe I mistake) I cannot use the only gnatlink because I need to link all other functions I already have.
Here is reported a minimal reproducible example.
--- main.c ---
#include <stdio.h>
extern int adainit();
extern int adafinal();
extern int Add(int,int);
int main()
{
adainit();
printf ("Sum of 3 and 4 is: %d\n", Add (3,4));
adafinal();
return 0;
}
--- lib_test.adb ---
package body Lib_Test is
function Ada_Add (A, B : Integer) return Integer is
begin
return A + B;
end Ada_Add;
end Lib_Test;
--- lib_test.ads ---
package Lib_Test is
function Ada_Add (A, B : Integer) return Integer;
pragma Export (C, Ada_Add, "Add");
end Lib_Test;
1° test: if you compile by using the following commands:
gcc -c main.c
gnatmake -c lib_test.adb
gnatbind -n lib_test.ali
gnatlink lib_test.ali main.o -o exe
and run ./exe you get Sum of 3 and 4 is: 7.
2° test: I tried to use the following CMake file (CMakeLists.txt) linking the *.a
cmake_minimum_required(VERSION 2.6)
project(Ada2C)
enable_language(C)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -m64")
find_library(TEST_lib lib_test.a PATHS ${CMAKE_CURRENT_SOURCE_DIR})
message(STATUS "Finding library: ${TEST_lib}")
add_executable(TEST_release ${CMAKE_CURRENT_SOURCE_DIR}/main.c)
target_link_libraries(TEST_release ${TEST_lib})
I generate library lib_test.a for the Ada function
gnatmake lib_test.adb
ar rc lib_test.a
I run the cmake and make and I get
main.c:(.text.startup+0x16a): undefined reference to adainit
main.c:(.text.startup+0x179): undefined reference to adafunction
main.c:(.text.startup+0x190): undefined reference to adafinal
More of a comment than an answer, but too long for a comment, so here goes:
Compiling Ada code into your binary means that your binary needs access to the GNAT runtime. This is one thing gnatlink does when you use it to link the final executable. The other thing is the b~<something>.ad{s,b} source gnatbind generates which you need to compile and link against as others mentioned.
The cleanest way to embed Ada in C I've seen so far is to create an encapsulated library. This probably does not make sense if your actual problem is with only one Ada function, but it does with larger chunks of Ada. The encapsulated library will be a shared library that has GNAT's runtime baked in. Being a shared library enables it to implicitly handle initialization during library loading so you don't need adainit() / adafinal() anymore.
The easiest way to create an encapsulated library is to use a ada_code.gpr file:
project ada_code is
for Library_Name use "mylib";
for Library_Dir use "lib";
for Library_Kind use "relocatable";
for Library_Standalone use "encapsulated";
for Library_Auto_Init use "true";
for Library_Interface use ("All", "Packages", "In.Your", "Ada.Code");
for Source_Dirs use ("adasrc");
end ada_code;
In CMake, you can then do:
# tell CMake how to call `gprbuild` on the `.gpr` file.
# you may need to replace `gprbuild` with the absolute path to it
# or write code that finds it on your system.
add_custom_target(compile_mylib
COMMAND gprbuild -P ada_code.gpr)
# copy the library file generated by gprbuild to CMake's build tree
# (you may skip this and just link against the file in the source tree)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/mylib.so
DEPENDS compile_mylib
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_SOURCE_DIR}/lib/mylib.so
${CMAKE_CURRENT_BINARY_DIR}/mylib.so)
# ... snip ...
# link to the copied library
# I am not 100% sure this adds the correct dependency to the custom command.
# You may need to experiment a bit yourself
target_link_libraries(TEST_release ${CMAKE_CURRENT_BINARY_DIR}/mylib.so)
In your C file, you can then delete everything related to adainit() and adafinal().

How to use a compiled C .so file in rust

has updated because of some suggestions
System:macOS 10.14.6
The question I want to ask here is how do I use rust to call a compiled .so file, sorry, I am new to this part.
I have a very simple c file:
#include "add.h"
int add(int a, int b) {
return a + b;
}
Then I used gcc-fPIC -shared -o libadd.so add.c to compile it into a .so file and put it in the lib directory
Then I wrote this in rust's build.rs file:
use std::env;
use std::path::{Path};
fn main() {
let pwd_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let path = Path::new(&*pwd_dir).join("lib");
println!("cargo:rustc-link-search=native={}", path.to_str().unwrap());
println!("cargo:rustc-link-lib=dylib=add");
// println!("cargo:rustc-link-lib=static=add");
// println!("cargo:rerun-if-changed=src/hello.c");
}
I expect I can get and use this function, main.rs is:
extern { fn add(a: i32, b: i32) -> i32; }
fn main() {
let c = unsafe { let d = add(3, 5); d };
println!("c: {:?}", c);
}
cargo build is ok, but cargo run with error:
Compiling hello-from-generated-code-3 v0.1.0 (/Users/niexiaotao/work/rust-server/rust-ffi/hello-from-generated-code-3)
Finished dev [unoptimized + debuginfo] target(s) in 0.34s
Running `target/debug/hello-from-generated-code-3`
dyld: Library not loaded: libadd.so
Referenced from: /Users/niexiaotao/work/rust-server/rust-ffi/hello-from-generated-code-3/target/debug/hello-from-generated-code-3
Reason: image not found
[1] 81811 abort cargo run
other thing: I change the .so to .a and cargo run is ok.
Sample code here
Thank you for your help!
I ran into the same problem. It seems that Cargo can properly build your library because you're explicitly telling it where to look for the file (using rustc-link-search). However, when you go to run it, Linux doesn't know where it is.
If you were to run ldd on your compiled Rust binary, you'd get something like this:
$ ldd target/debug/hello-from-generated-code-3
linux-vdso.so.1 (0xabcd)
libadd.so => not found <----- ldd can't find your library
...
This is because your .so file isn't found in LD_LIBRARY_PATH. You can fix this either by copying your library into a relevant folder or just setting it when you run your program. For example, you should be able to say:
LD_LIBRARY_PATH=. cargo run
(Or any other path where your .so file is - not necessarily .)
I didn`t use env variables at all. For instance you have /your-crate/lib/libmystuff.so shared library. In order to link it, specify the following:
println!("cargo:rustc-link-search=native=./lib");
println!("cargo:rustc-link-lib=dylib=mystuff");
Pay attention, library without prefix lib, and path is relative.
And just in case. You can also define include folder with your header files:
let headers = Path::new("/headers");
cc::Build::new()
.file("src/foo.c")
.include(headers)
.compile("foo");
Tested on Linux. Best regards.

Compile C code and expose it to Swift under Linux

Is there a way to compile native C or C++ code and expose it to Swift on Linux? I can see that several Apple libraries like libdispatch are written in pure C and that you can access them in Swift just by importing them.
To set the example let's say that I have two files Car.c and Car.h that define structure named Car. Is there a way that I can compile them and use them in Swift by writing import statement?
import Car
I've tried writing module.modulemap file inside directory where .c, .h and Package.swift files are located:
module Car {
header "Car.h"
export *
}
and running swift build. This yield error:
<unknown>:0: error: unexpected 'commands' value (expected map)
<unknown>:0: error: unable to load build file
I'm using Swift version 3.0-dev (March 24 2016)
[Update 1]
I've contacted Max(mxcl) - one of the creators of Swift Package Manager and he told me to get rid of the modulemap and put the .c and .h files directly in Sources folder. After I did that package compiled but it's not available as module. Also I can't call any of the defined functions in the .h file.
If you build a library out of your C code, you can create a system module for it, which can then be imported into Swift, see this answer: Use a C library in Swift on Linux.
Another way to approach this task is to create a bridging header, as suggested by #Philip. Here is an oversimplified example. Let's consider the following C code:
/* In car.h */
int getInt();
/* In car.c */
int getInt() { return 123; }
We will use car.h as the bridging header. The swift source is (in file junk.swift):
print("Hi from swift!")
var i = getInt()
print("And here is an int from C: \(i)!")
First, create an object file, car.o, from car.c:
gcc -c car.c
Now build an executable, junk, as follows:
swiftc -import-objc-header car.h junk.swift car.o -o junk
Running the executable gives:
$ ./junk
Hi from swift!
And here is an int from C: 123!
The -import-objc-header option is hidden. To see it and a bunch of other hidden options, run:
swiftc -help-hidden
I did this using Swift 3.0 development snapshot for Ubuntu 14.04 from April 12, available here: https://swift.org/builds/development/ubuntu1404/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a/swift-DEVELOPMENT-SNAPSHOT-2016-04-12-a-ubuntu14.04.tar.gz
Now, if you want to use C++, you will need to create a wrapper, written in a C++ source file and compiled with a C++ compiler, but with functions callable from C by using extern "C". Those functions can then be called from Swift as any C function. See, for example, this answer: Can I mix Swift with C++? Like the Objective - C .mm files
Using C functions in swift requires a bridging header that includes all the C functionality you need. For example, myBridgingHeader.h which contains #include "Car.h"and whatever other C stuff you want. I believe C++ is currently not supported.
Once you have the bridging header you need to make swift aware of it. Xcode users get this for free when they add it to the project. In Linux, use '-import-objc-header /path/to/header' flag when compiling.
Edit: I've added a complete example below consisting of 6 files for any others who may have this question. It's basically the same as the one above but I didn't see that til I had already put it together haha. Also, it maybe useful for someone who needs to link against static libraries.
Copy the file contents below to appropriately named files, make, then ./hello and that should work. For the record, I've only run this on swift version 2.2-dev (use swift --version to check yours)
hello.swift:
let n: Int32 = 5
print("Hello, Swift World!")
print("mult2(\(n,N)) = \(mult2(n,N))")
print("CONST1=\(CONST1), CONST2=\(CONST2), CONST3=\(CONST3)")
bridge.h:
#include "defs.h"
#include "mult.h"
defs.h:
#define CONST1 1
#define CONST2 2
#define CONST3 3
mult.h:
#define N 7
int mult2(int,int);
mult.c:
#include "defs.h"
#include "mult.h"
int mult2(int a, int b)
{
return a*b;
}
Makefile:
all: hello
hello: libmult.a
swiftc hello.swift -import-objc-header ./bridge.h -L. -lmult -o hello
libmult.a: mult.o
ar -rc libmult.a mult.o
ranlib libmult.a
mult.o: mult.c mult.h defs.h
gcc -c mult.c -o mult.o
.PHONY: clean
clean:
rm -f *.o *.a hello

Linking glfw and D

I'm trying to use glfw 2.7.8 with the Digital Mars D compiler version 2.0.
I followed the instructions from the example makefile on copying the .lib files to dm/lib folder, but I have not had success.
An example compile looks like this.
dmd main.d
The source of the file is:
import std.string;
import glfw;
int main()
{
glfwInit();
return 0;
}
The output I get is
main.d(2): Error: module glfw is in file 'glfw.d' which cannot be read
import path[0] = /usr/share/dmd/src/phobos
import path[1] = /usr/share/dmd/src/druntime/import
I have tried on both Windows 7 and Mac OSX 10.8.2, but I have not had success. Should I compile glfw.d as a lib and then drop it into my main directory?
I have also had _symbol not found messsages when I try to drop the .lib into the main directory and use the -L compiler flags linking to glfw.lib.
Any examples or help would be much appreciated.
you need to add -Ipath/to/glfw-x.x.x/support/d/imports to the compiler directive
you can also add the lib folder (with the def files) with the -L switch so the linker can map the symbols properly to the ones exposed in the dll
You need to pass glfw.d to the compiler:
dmd main.d glfw.d
assuming it is in the same directory as main.d.

Resources