Clang vs CBindingGen

,

Also, the Julia package curators are not a fan of the C.jl package name, so package functionality will probably be merged into CBinding.jl

And for historical reasons, the name of Clang.jl is also to some extent misleading. Clang.jl should only host a thin wrapper over libclang or even Clang, but I guess it’s too late to make the change. :sweat_smile:

Hah! “Thin” is a subjective term, so no worries!

1 Like

For someone who might be interested in the interop between Julia and C, here are some recent PRs/Issues related to the topic:

2 Likes

Off-topic, but I’ve been waiting for #32658 to finally be merged for ages. It’s really slightly embarrassing that Julia still has no simple, straightforward way to declare mutually recursive types.

1 Like

The raylib bindings using CBinding.jl v1.0:

module libraylib
	using CBinding
	
	let
		CMAKE_INSTALL_PREFIX = "$(dirname(@__DIR__))/deps/usr"
		c`-I$(CMAKE_INSTALL_PREFIX)/include -L$(CMAKE_INSTALL_PREFIX)/lib -lraylib`
	end
	
	const c"va_list" = Cvoid
	c"""
		#include <raylib.h>
		#include <physac.h>
		#include <raymath.h>
		#include <rlgl.h>
	"""ji
end

You can add ‘w’ to the string macro options to wrap the inline functions as well, but that is more experimental functionality.

2 Likes

That’s also quite lean! But if I understand the documentation correctly the generated bindings have to be used via the c"" string macro, right?

With the ‘j’ string macro option applied, Julian names are also created. So in this example you can directly use libraylib.Transform for instance.

As you mention, bindings are always generated in the c"..." symbol “namespace” to avoid name collisions (that approach is 100% foolproof). They are optionally mapped into the Julian symbol “namespace” if the user determines no name collisions will occur and requests that functionality.

2 Likes

@mhinsch I’m also interested in an Raylib binding. If you have something working would you mind uploading it to GitHub?

1 Like

Unfortunately more urgent (as in I’m actually getting paid to do them) things got in the way and will do so for a while. I still intend to get back to the raylib wrapper, but it might be a couple of months before I have the time to do so.

Do you have any unfinished code you could upload?

I might want to take part in the Game Makers Toolkit game jam next month and if i don’t have to start from scratch that might motivate me to get something runable by then.

Not really anything beyond a basic proof of concept. I basically used Gnumic’s example:

wrap.toml

[general]
library_name = ":libraylib"
output_file_path = "./LibRaylib.jl"
module_name = "LibRaylib"
use_julia_native_enum_type = true

wrap.jl

using Clang.Generators

cd(@__DIR__)

const RAYLIB_H = ["/usr/local/include/raylib.h"]

options = load_options(joinpath(@__DIR__, "wrap.toml"))

args = String[]

ctx = create_context(RAYLIB_H, args, options)

build!(ctx)

Running wrap.jl produces LibRayLib.jl which is 99% fine, but needs a tiny bit of editing to account for issues with vararg functions and some macros (no idea if that has been fixed in the meantime). Then code like this works:

run_rl.jl



include("LibRaylib.jl")

const RL = LibRaylib

const screen_width = 1600
const screen_height = 900

function run()
	RL.InitWindow(screen_width, screen_height, "run")

	camera = RL.Camera2D(RL.Vector2(500, 500), 
		RL.Vector2(screen_width/2, screen_height/2),
		0.0,
		1.0)

	x = floor(Int, rand() * 1000) + 200
	y = floor(Int, rand() * 700) + 100

	while RL.WindowShouldClose() == 0
		RL.BeginDrawing()
		
		RL.BeginMode2D(camera)
		RL.ClearBackground(RL.Color(0, 0, 0, 255))

		x += floor(Int, (rand()-0.49)*10)
		y += floor(Int, (rand()-0.49)*10)

		RL.DrawText("Dies ist ein Test!", 200, 200, 20, RL.Color(250, 0, 0, 255))
		RL.DrawCircle(x, y, 100, RL.Color(0, 255, 0, 150))

		RL.EndMode2D()




		RL.EndDrawing()
	end

	RL.CloseWindow()
end


run()
1 Like

Hi, thank you for the thorough examples of using clang. However, I have an issue running the same code as the above.

The LibRaylib.jl is created successfully, but while running the run_rl.jl, I got this error ERROR: could not load library "libraylib".

Any tips to solve this issue?

It seems like you are trying to use a wrapper for a library, but without the library. Are you sure that libraylib is installed on your system?

Hi @serenity4, thank you for responding.

I have installed the raylib (following through this guide Working on GNU Linux · raysan5/raylib Wiki · GitHub), it still gave me the same error.

git clone https://github.com/raysan5/raylib.git raylib
cd raylib
mkdir build && cd build
cmake -DBUILD_SHARED_LIBS=ON ..
make
sudo make install

I am not sure why it doesn’t detect the installation. I am a new Linux user, so maybe I didn’t install it correctly.

You should then make sure that the libraries you built are discoverable within the library search path of your system. You can look into LD_LIBRARY_PATH for example.

Since Raylib is in Yggdrasil (see here), an even better solution would be to use the Julia package Raylib_jll. As long as the library name of the JLL package matches the library name you use in your wrapper, the issue might be solved by just doing import Raylib_jll (at package __init__() time, Raylib_jll will dlopen the shared library and therefore the library will be callable by name directly, i.e. no need to mess with system paths).

1 Like

Thanks, @serenity4!

I will figure out how to continue with this one. However, I tried with a different approach using the JLL package.

using Clang.Generators
using Raylib_jll

cd(@__DIR__)

include_dir = normpath(Raylib_jll.artifact_dir, "include")

# wrapper generator options
options = load_options(joinpath(@__DIR__, "wrap.toml"))

# add compiler flags, e.g. "-DXXXXXXXXX"
args = get_default_args()
push!(args, "-I$include_dir")

header_dir = include_dir
headers = [joinpath(header_dir, header) for header in readdir(header_dir) if endswith(header, ".h")]

# create context
ctx = create_context(headers, args, options)

# run generator
build!(ctx)
[general]
library_name = "libraylib"
output_file_path = "./LibRayLib.jl"
module_name = "LibRaylib"
jll_pkg_name = "Raylib_jll"

However, I got this error message

[ Info: Processing header: /home/vscode/.julia/artifacts/43bf856bd341c937a4ef62a0db273d61985df75e/include/rlgl.h
ERROR: AssertionError: duplicated definitions should be exactly the same!
Stacktrace:
 [1] (::IndexDefinition)(dag::ExprDAG, options::Dict{String, Any})
   @ Clang.Generators ~/.julia/packages/Clang/i9s1u/src/generator/passes.jl:127
 [2] build!(ctx::Context, stage::Clang.Generators.BuildStage)
   @ Clang.Generators ~/.julia/packages/Clang/i9s1u/src/generator/context.jl:169
 [3] build!(ctx::Context)
   @ Clang.Generators ~/.julia/packages/Clang/i9s1u/src/generator/context.jl:160
 [4] top-level scope
   @ /workspaces/Docker/Julia/Raylib/jll/wrap.jl:22

Do you have any idea why this is happening?

If you want to use raylib with Julia, someone already made a Julia wrapper for it:

chengchingwen/Raylib.jl: Julia wrapper for the raylib videogames programming library (github.com)

It works pretty well. See this example:

NanoVG.jl/raylib.jl at main · dylanxyz/NanoVG.jl (github.com)

2 Likes

Hi @dylanxyz, thank you for the response :slight_smile:

What I am trying to do here is to learn about using Clang.jl as a wrapper. Because I stumbled upon a problem when I was trying to make a wrapper for cuQuantum. There is already JLL for this: GitHub - JuliaBinaryWrappers/cuQuantum_jll.jl. But I think something is missing, which makes an error.

So, technically it should be easier for me to build it. But, as I am still new to Julia and cuQuantum, I want to see more examples of how Clang.jl can be used.

If you have any information that someone is already made the wrapper for this project, it would be awesome :slight_smile:

Fixed by Fix an oversight of #393 · JuliaInterop/Clang.jl@b5b2b58 · GitHub

1 Like