From 07fc701e499590f32570b5de88655ea6561c0fce Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Wed, 21 Sep 2022 23:23:08 +0200 Subject: [PATCH 1/9] Reworked the book/tutorial --- book/src/SUMMARY.md | 12 +- book/src/{config_api.md => config/api.md} | 0 book/src/{config_ffi.md => config/ffi.md} | 10 - .../introduction.md} | 0 .../name_override.md} | 0 book/src/generate_docs.md | 32 -- book/src/introduction.md | 38 +- book/src/tutorial/finding_gir_files.md | 26 + book/src/tutorial/generate_docs.md | 36 ++ book/src/tutorial/handling_errors.md | 38 +- book/src/tutorial/high_level_rust_api.md | 489 ++++++++++-------- book/src/tutorial/introduction.md | 13 +- book/src/tutorial/preparation.md | 67 +++ book/src/tutorial/sys_library.md | 142 ++--- 14 files changed, 488 insertions(+), 415 deletions(-) rename book/src/{config_api.md => config/api.md} (100%) rename book/src/{config_ffi.md => config/ffi.md} (92%) rename book/src/{config_introduction.md => config/introduction.md} (100%) rename book/src/{config_name_override.md => config/name_override.md} (100%) delete mode 100644 book/src/generate_docs.md create mode 100644 book/src/tutorial/finding_gir_files.md create mode 100644 book/src/tutorial/generate_docs.md create mode 100644 book/src/tutorial/preparation.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 289beeebb..e9348214b 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -1,12 +1,14 @@ # Summary - [Introduction](introduction.md) -- [Configuration](config_introduction.md) - - [FFI Options](config_ffi.md) - - [API Options](config_api.md) - - [Crate name override](config_name_override.md) - [Tutorial](tutorial/introduction.md) + - [Preparation](tutorial/preparation.md) + - [Finding .gir files](tutorial/finding_gir_files.md) - [Generating the FFI library](tutorial/sys_library.md) - [Generating the Rust API](tutorial/high_level_rust_api.md) - [Handling generation errors](tutorial/handling_errors.md) -- [Generating documentation](generate_docs.md) + - [Generating documentation](tutorial/generate_docs.md) +- [Configuration files](config/introduction.md) + - [FFI Options](config/ffi.md) + - [API Options](config/api.md) + - [Crate name override](config/name_override.md) diff --git a/book/src/config_api.md b/book/src/config/api.md similarity index 100% rename from book/src/config_api.md rename to book/src/config/api.md diff --git a/book/src/config_ffi.md b/book/src/config/ffi.md similarity index 92% rename from book/src/config_ffi.md rename to book/src/config/ffi.md index e42c35d6b..6aaff6017 100644 --- a/book/src/config_ffi.md +++ b/book/src/config/ffi.md @@ -52,16 +52,6 @@ dependencies = [ ] ``` -You can mark some functions that has suffix `_utf8` on Windows: - -```toml -[[object]] -name = "GdkPixbuf.PixbufAnimation" -status = "generate" - [[object.function]] - name = "new_from_file" -``` - Also, you can add rust cfg conditions on objects, functions and constants, for example, when flagging for conditional compilation: ```toml diff --git a/book/src/config_introduction.md b/book/src/config/introduction.md similarity index 100% rename from book/src/config_introduction.md rename to book/src/config/introduction.md diff --git a/book/src/config_name_override.md b/book/src/config/name_override.md similarity index 100% rename from book/src/config_name_override.md rename to book/src/config/name_override.md diff --git a/book/src/generate_docs.md b/book/src/generate_docs.md deleted file mode 100644 index 832d34ccc..000000000 --- a/book/src/generate_docs.md +++ /dev/null @@ -1,32 +0,0 @@ -# Generating documentation - -And finally the last feature! Just run the following command (note the `-m doc` at the end): - -```sh -cargo run --release -- -c YourGirFile.toml -d ../gir-files --doc-target-path the-output-file-name -m doc -``` - -It'll generate a markdown file if everything went fine. That's where all this crate's documentation is. If you want to put it back into your crate's source code like "normal" doc comments, run: - -```sh -cargo install rustdoc-stripper -rustdoc-stripper -g -o docs.md -``` - -And now your crate should be completely documented as expected! - -If you defining traits manually you can add them to "Implements" section for classes and interfaces: - -```toml -[[object]] -name = "Gtk.Assistant" -status = "generate" -#add link to trait from current crate -manual_traits = ["AssistantExtManual"] - -[[object]] -name = "Gtk.Application" -status = "generate" -#add link to trait from other crate -manual_traits = ["gio::ApplicationExtManual"] -``` diff --git a/book/src/introduction.md b/book/src/introduction.md index b6d0ba5c8..78d11c916 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -1,31 +1,31 @@ # Introduction +[gir] is a tool to automatically generate bindings and a safe wrapper for a library written in C. All you need to be able to use it, is a `.gir` file containing the [GObject introspection](https://gi.readthedocs.io/en/latest/) data for the library you want to create the bindings for, as well as the `.gir` files for all its dependencies. Have a look at the tutorial if you don't know how to [find the .gir files](tutorial/finding_gir_files.md). If your library does not provide a `.gir` file, unfortunately you cannot use [gir], but maybe you can try [rust-bindgen](https://github.com/rust-lang/rust-bindgen). -This book aims to provide complete documentation on how to use [gir] to generate bindings for GObject based libraries based on the [GObject introspection](https://gi.readthedocs.io/en/latest/) data. +This book contains a tutorial on how to use gir. As an example we will create the bindings for pango. In many cases you will be able to follow the same steps with your library. If you are already familiar with gir and you just want to look up details about the configuration files, feel free to skip ahead to the documentation of the [configuration files](config/introduction.md). -[gir] consists of a library and at the same time a binary you can use to generate the bindings. +## General steps +[gir] tries to make it as simple as possible to generate a safe wrapper for your C library. The process can be divided into four steps that correspond to the four operating modes gir has. -[gir] requires both a `Gir.toml` configuration file and a `.gir` file containing the GObject introspection data. +- Generating unsafe bindings: +In this step, the low-level FFI bindings are created from the supplied `*.gir` file. These are essentially direct calls into the related C library and are typically unsafe. The resulting crate is typically appended with -sys. The operating mode is `sys`. -- The `*.gir` you need will correspond to the project you want to generate bindings for. You can get them from here or directly on ubuntu website (for example: ). +- Generating a safe wrapper: +Next, another crate for a layer on top of these unsafe (sys) bindings is created, which makes them safe for use in general Rust. The operating mode is `normal`. -- The `*.toml` is what is used to pass various settings and options to [gir] for use when generating the bindings - you will need to write one to suit your needs, for an example you can take a look to gtk-rs/sys/gir-gtk.toml. +- Checking for missing types/methods: +The operating mode `not_bound` allows you to see the detected types/methods that will not be generated for whatever reasons. -It operates on 4 different modes: +- Adding documentation: +After the safe wrapper is created, gir can even generate the documentation for us. Use the operating mode `doc` to do so. -- `sys`: is what creates the low-level FFI bindings from the supplied `*.gir` file - these are essentially direct calls in to the related C library and are typically unsafe. The resulting crate is typically appended with -sys. -- `normal`: generates another crate for a layer on top of these unsafe (sys) bindings which makes them safe for use in general Rust. - -- `not_bound`: allows you to see the detected types/methods that will not be generated for whatever reasons. - -- `doc`: used for documentation generation - -[gir]: https://github.com/gtk-rs/gir - -## Helpers - -[gir] includes a wrapper script `./generator.py` that detects `Gir.toml` configurations in the current directory (or the path(s) passed on the command-line) and generates "normal" or "sys" crates for it. Alternatively --embed-docs can be passed to prepare source-code for a documentation build by moving all documentation into it. For a complete overview of available options, pass --help. +## Regenerating the bindings and wrapper +In order to generate the bindings and the wrapper for the first time, the above mentioned steps should be followed. When you want to regenerate the crates because e.g. the library was updated, you can simplify the process by running the helper script `./generator.py`. The script detects `Gir.toml` configurations in the current directory (or the paths passed on the command-line) and generates "normal" or "sys" crates for it. Alternatively --embed-docs can be passed to prepare source-code for a documentation built by moving all documentation into it. For a complete overview of available options, pass --help. ## GIR format reference - It can always be useful to look at the [reference](https://gi.readthedocs.io/en/latest/annotations/giannotations.html) or [schema](https://gitlab.gnome.org/GNOME/gobject-introspection/blob/master/docs/gir-1.2.rnc). + +## Contact us +If you use gir on another library and it fails and you can't figure out why, don't hesitate to [contact us](https://gtk-rs.org/contact)! + +[gir]: https://github.com/gtk-rs/gir \ No newline at end of file diff --git a/book/src/tutorial/finding_gir_files.md b/book/src/tutorial/finding_gir_files.md new file mode 100644 index 000000000..4215e46f1 --- /dev/null +++ b/book/src/tutorial/finding_gir_files.md @@ -0,0 +1,26 @@ +# Where can I find those .gir files? +There are multiple ways you can get the needed `.gir` files. The `*.gir` you need corresponds to the library you want to generate bindings for. If you have the library installed, you can search for the `.gir` file under `/usr/share/gir-1.0/`. Having the library installed is a good idea so that you can test the generated code. Otherwise you should be able to get it from the package that installs the library. Ubuntu for example allows you to [search](https://packages.ubuntu.com/) for packages and download them via their website. + +Have a look at the .gir file of your library. At the beginning of the file, you probably see something similar to ``. "GObject" in this case would be a dependency and you will have to find the .gir file for your dependencies as well. + +## GTK dependencies +If your library depends on GTK libraries, the recommended way to get the `.gir` files for them is to clone the [gir-files repo](https://github.com/gtk-rs/gir-files). It's the recommended way, because some of the `.gir` files included in the libraries are invalid (missing or invalid annotations for example). These errors are already fixed in the gir files from the repo. Otherwise you could use the above mentioned methods to find the files and run the [script](https://github.com/gtk-rs/gir-files/blob/master/fix.sh) to fix the `.gir` files available in the gir-files repository (and only them!). You can run it like this (at the same level of the `.gir` files you want to patch): + +```sh +sh fix.sh +``` + +## GStreamer dependencies +For GStreamer related dependencies, follow the above mentioned steps but clone this [repo](https://gitlab.freedesktop.org/gstreamer/gir-files-rs) instead. + +## Other dependencies +If you have other dependencies, you have to find the files yourself. They can often be found in the repo containing the source of your dependencies or if you have them installed, you might find them under `/usr/share/gir-1.0/` again. + +## Example +We want to generate the wrapper for pango. It is related to GTK, so in order to get its .gir files, we use the recommended way. While being in the project folder `git-tutorial`, we clone the [gir-files repo](https://github.com/gtk-rs/gir-files). + +```sh +git clone --depth 1 https://github.com/gtk-rs/gir-files +``` + +If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. That's the one for pango. Because we already cloned the gir-files repo, we also have all the other .gir files of the dependencies that we need. Now we can create the unsafe bindings. \ No newline at end of file diff --git a/book/src/tutorial/generate_docs.md b/book/src/tutorial/generate_docs.md new file mode 100644 index 000000000..27595281e --- /dev/null +++ b/book/src/tutorial/generate_docs.md @@ -0,0 +1,36 @@ +# Generating documentation +And finally the last feature! Just run the following command in the folder of the safe wrapper crate: + +```sh +gir -c Gir.toml -d ../gir-files --doc-target-path docs.md -m doc +``` + +* `-d ../gir-files`: flag to select the folder containing the .gir files. +* `--doc-target-path docs.md`: flag to select the name of the markdown file containing the documentation. +* `-m doc`: flag to select the work mode for generating the documentation. + +It'll generate a markdown file if everything went fine. It contains all of the crate's documentation. If you want to put it into your crate's source code like "normal" doc comments, run: + +```sh +cargo install rustdoc-stripper +rustdoc-stripper -g -o docs.md +``` + +And now your crate should be completely documented as expected! + +Running the above commands again would duplicate the doc comments. Make sure to first remove the old ones before running the command again. You can do this by running the following commands: + +```sh +rustdoc-stripper -s -n +rustdoc-stripper -g -o docs.md +``` + +Try building the documentation and also try it with the various features you might have + +```sh +cargo doc +``` + +Congratulations, we are done! You have successfully created the safe wrapper for a C library! + +You can easily publish your generated bindings and the wrapper to crates.io to allow others to use it. Publishing crates is easy but keep in mind that they need to be maintained as well. We set up the project folder in a way that easily allows sharing the code. All that is needed is to add some information to your Cargo.toml. Gir will not override them when you re-generate bindings. Easy, right? If this is your first time publishing a crate, you can find a detailed guide [here](https://doc.rust-lang.org/cargo/reference/publishing.html). \ No newline at end of file diff --git a/book/src/tutorial/handling_errors.md b/book/src/tutorial/handling_errors.md index 6bf2b8848..8ce7f6b86 100644 --- a/book/src/tutorial/handling_errors.md +++ b/book/src/tutorial/handling_errors.md @@ -1,12 +1,12 @@ # Handling generation errors -There are a few kinds of errors (not much luckily) which can happen with [gir] generation. Let's take a look at them. +Luckily there are only a few errors which can happen with [gir] generation. Let's take a look at them. ### Cannot find macros Compilation of the generated bindings may fail with errors like the following: -``` +```console error: cannot find macro `skip_assert_initialized` in this scope --> src/auto/enums.rs:83:9 | @@ -24,7 +24,7 @@ error: cannot find macro `assert_initialized_main_thread` in this scope = help: have you added the `#[macro_use]` on the module/import? ``` -In this case you’ll have to implement them yourself. For example, you can add the following to your `lib.rs` file: +In this case you’ll have to implement them yourself. Macros are order-dependent and you *must* insert this code before declaring modules that use it (e.g. `mod auto`). For example, you can add the following to your `lib.rs` file: ```rust /// No-op. @@ -46,11 +46,10 @@ macro_rules! assert_initialized_main_thread { } ``` -Note: macros are order-dependent and you *must* insert this code before declaring modules that use it (e.g. `mod auto`). One complication here is that the `assert_initialized_main_thread!` macro depends on the exact library. If it's GTK-based then the above macro is likely correct, unless the library has its own initialization function. If it has its own initialization function it would need to be handled in addition to GTK's here in the same way. -For non-GTK-based libraries this macro would handle the initialization function of that library in the same way, or if there is none would simply do nothing: +For non-GTK-based libraries the following macro would handle the initialization function of that library in the same way, or if there is none it would simply do nothing: ```rust /// No-op. @@ -63,12 +62,11 @@ macro_rules! assert_initialized_main_thread { If [gir] generation fails (for whatever reason), it means you'll have to implement the type yourself. Just like types from other `gtk-rs` crates, you'll need to put it into the "manual" list. Then you need to put the type into the `src` folder (or inside a subfolder, you know how Rust works). -/!\ Don't forget to reexport the type inside your `src/lib.rs` file! For example, let's take a look at the [requisition.rs](https://github.com/gtk-rs/gtk/blob/master/src/requisition.rs) file from the `gtk` crate. +/!\ Don't forget to reexport the type inside your `src/lib.rs` file! For example, let's take a look at the [requisition.rs](https://github.com/gtk-rs/gtk3-rs/blob/master/gtk/src/requisition.rs) file from the `gtk3` crate. Since it's a "simple" type (no pointer, therefore no memory management to do), [gir] doesn't know how to generate it. You'll need to implement some traits by hand like `ToGlibPtr` or `ToGlibPtrMut` (depending on your needs). ## Bad function generation - In some cases, the generated code isn't correct (array parameters are often an issue). In such cases, it's better to just make the implementation yourself. As an example, let's say you want to implement `Region::is_empty` yourself. A few changes have to be made. Let's start with `Gir.toml`: ```toml @@ -84,9 +82,9 @@ status = "generate" ignore = true ``` -So to sum up what I wrote above: we removed "GtkSource.Region" from the "generate" list and we created a new entry for it. Then we say to [gir] that it should generate (through `status = "generate"`). However, we also tell it that we don't want the "is_empty" to be generated. +So to sum up what was written above: we removed "GtkSource.Region" from the "generate" list and we created a new entry for it. Then we say to [gir] that it should generate (through `status = "generate"`). However, we also tell it that we don't want the "is_empty" function to be generated. -Now that we've done that, we need to reimplement it. Let's create a `src/region.rs` file: +Now that we've done that, we need to implement it. Let's create a `src/region.rs` file: ```rust use glib::object::IsA; @@ -107,11 +105,10 @@ impl> RegionExtManual for O { You might wonder: "why not just implementing it on the `Region` type directly?". Because like this, a subclass will also be able to use this trait easily as long as it implements `IsA`. For instance, in gtk, everything that implements `IsA` (so almost every GTK types) can use those methods. -As usual, don't forget to reexport the trait. A little tip about reexporting manual traits: in `gtk-rs`, we create a `src/prelude.rs` file which reexports all traits (both manual and generated ones), making it simpler for users to use them through `use [DEPENDENCY]::prelude::*`. It looks like this: +As usual, don't forget to reexport the trait. A little tip about reexporting manual traits: in `gtk3-rs`, we create a `src/prelude.rs` file which reexports all traits (both manual and generated ones), making it simpler for users to use them through `use [DEPENDENCY]::prelude::*`. The `src/prelude.rs` file looks like this: ```rust pub use auto::traits::*; - pub use region::RegionExtManual; ``` @@ -119,8 +116,25 @@ Then it's reexported as follows from the `src/lib.rs` file: ```rust pub mod prelude; - pub use prelude::*; ``` +## Manually defined traits missing from the documentation +If you defined traits manually, you can add them to the "Implements" section in the documentation for classes and interfaces by using the `manual_traits = []` option in the `Gir.toml` file. Here is an example: + +```toml +[[object]] +name = "Gtk.Assistant" +status = "generate" +#add link to trait from current crate +manual_traits = ["AssistantExtManual"] + +[[object]] +name = "Gtk.Application" +status = "generate" +#add link to trait from other crate +manual_traits = ["gio::ApplicationExtManual"] +``` + + [gir]: https://github.com/gtk-rs/gir diff --git a/book/src/tutorial/high_level_rust_api.md b/book/src/tutorial/high_level_rust_api.md index 0d45055f1..a733a0651 100644 --- a/book/src/tutorial/high_level_rust_api.md +++ b/book/src/tutorial/high_level_rust_api.md @@ -1,278 +1,317 @@ # Generating the Rust API +In the previous step we successfully created the unsafe bindings of the -sys crate. We are now in the directory of the safe wrapper crate (`gir-tutorial/pango`). -Time to go back to the "global" sourceview folder: - -```sh -cd .. -``` - -As you certainly guessed, we'll need a new `Gir.toml` file. Let's write it: +## The Cargo.toml file +The Cargo.toml file will not be replaced when you run gir. So it is our responsibility to make sure the information in it is correct. Open the Cargo.toml file and have a look at it. Make sure everything under `[package]` is to your liking. +Add the following lines to the file: ```toml -[options] -girs_directories = ["../gir-files"] -library = "GtkSource" -version = "3.0" -min_cfg_version = "3.0" -target_path = "." -work_mode = "normal" -generate_safety_asserts = true -deprecate_by_min_version = true -single_version_file = true - -generate = [] +[package.metadata.docs.rs] +features = ["dox"] ``` +This automatically activates the `dox` feature if you chose to publish the bindings and docs.rs tries to build the documentation. If you are not going to maintain the crate and don't want to publish it, this line is not going to hurt. -A few new things in here. Let's take a look at them: - -* `work_mode` value is now set to `normal`, it means it'll generate the high-level Rust api instead of the sys-level. -* `generate_safety_asserts` is used to generates checks to ensure that, or any other kind of initialization needed before being able to use the library. -* `deprecate_by_min_version` is used to generate a [Rust "#[deprecated]"](https://doc.rust-lang.org/edition-guide/rust-2018/the-compiler/an-attribute-for-deprecation.html) attribute based on the deprecation information provided by the `.gir` file. -* `single_version_file` is a very useful option when you have a lot of generated files (like we'll have). Instead of generating the gir hash commit used for the generation in the header of all generated files, it'll just write it inside one file, removing `git diff` noise **a lot**. -* `generate = []`: this line currently does nothing. We say to [gir] to generate nothing. We'll fulfill it later on. - -Let's make a first generation of our high-level Rust API! +We also need to add `libc`, `bitflags`, `glib` and `glib-sys` and all other dependencies we used in the sys crate as dependencies. Because we are creating a wrapper for the sys crate, which we generated in the previous chapter, we also need to add the sys crate to the list of dependencies. In the automatically generated code, the sys crate is always called `ffi`, so we need to rename the sys crate in our `Cargo.toml`. For our example, this results in the following dependencies: +```toml +[dependencies] +libc = "0.2" +bitflags = "1.0" -```sh -gir -o . -``` +[dependencies.ffi] +package = "pango-sys" +path = "./pango-sys" -Now if you take a look around, you'll see a new "auto" folder inside "src". Doesn't contain much though. Which makes sense since we're generating nothing. Time to introduce you to a whole new [gir] mode: `not_bound`. Let's give it a try: +[dependencies.glib] +package = "glib-sys" +git = "https://github.com/gtk-rs/gtk-rs-core" -```console -> gir -o . -m not_bound -[NOT GENERATED] GtkSource.Buffer -[NOT GENERATED PARENT] Gtk.TextBuffer -[NOT GENERATED] GtkSource.Language -[NOT GENERATED] GtkSource.Mark -[NOT GENERATED PARENT] Gtk.TextMark -[NOT GENERATED] GtkSource.StyleScheme -[NOT GENERATED] GtkSource.UndoManager -[NOT GENERATED] GtkSource.SortFlags -[NOT GENERATED] GtkSource.Completion -[NOT GENERATED PARENT] Gtk.Buildable -[NOT GENERATED] GtkSource.CompletionProvider -[NOT GENERATED] GtkSource.CompletionContext -[NOT GENERATED PARENT] GObject.InitiallyUnowned -[NOT GENERATED] GtkSource.CompletionInfo -[NOT GENERATED PARENT] Gtk.Window -[NOT GENERATED PARENT] Gtk.Bin -[NOT GENERATED PARENT] Gtk.Container -[NOT GENERATED PARENT] Gtk.Widget -[NOT GENERATED PARENT] Atk.ImplementorIface -[NOT GENERATED] GtkSource.View -[NOT GENERATED PARENT] Gtk.TextView -[NOT GENERATED PARENT] Gtk.Scrollable -[NOT GENERATED] GtkSource.CompletionActivation -[NOT GENERATED] GtkSource.CompletionProposal -[NOT GENERATED] GtkSource.CompletionError -[NOT GENERATED] GtkSource.CompletionItem -[NOT GENERATED PARENT] GtkSource.CompletionProposal -[NOT GENERATED] GtkSource.CompletionWords -[NOT GENERATED PARENT] GtkSource.CompletionProvider -[NOT GENERATED] GtkSource.DrawSpacesFlags (deprecated in 3.24) -[NOT GENERATED] GtkSource.Encoding -[NOT GENERATED] GtkSource.File -[NOT GENERATED] GtkSource.MountOperationFactory -[NOT GENERATED] GtkSource.FileLoader -[NOT GENERATED] GtkSource.FileLoaderError -[NOT GENERATED] GtkSource.FileSaver -[NOT GENERATED] GtkSource.FileSaverFlags -[NOT GENERATED] GtkSource.FileSaverError -[NOT GENERATED] GtkSource.Gutter -[NOT GENERATED] GtkSource.GutterRenderer -[NOT GENERATED] GtkSource.GutterRendererState -[NOT GENERATED] GtkSource.GutterRendererAlignmentMode -[NOT GENERATED] GtkSource.GutterRendererPixbuf -[NOT GENERATED PARENT] GtkSource.GutterRenderer -[NOT GENERATED] GtkSource.GutterRendererText -[NOT GENERATED] GtkSource.LanguageManager -[NOT GENERATED] GtkSource.Map -[NOT GENERATED PARENT] GtkSource.View -[NOT GENERATED] GtkSource.MarkAttributes -[NOT GENERATED] GtkSource.PrintCompositor -[NOT GENERATED] GtkSource.Region -[NOT GENERATED] GtkSource.RegionIter -[NOT GENERATED] GtkSource.SearchContext -[NOT GENERATED] GtkSource.SearchSettings -[NOT GENERATED] GtkSource.Style -[NOT GENERATED] GtkSource.SpaceDrawer -[NOT GENERATED] GtkSource.SpaceTypeFlags -[NOT GENERATED] GtkSource.SpaceLocationFlags -[NOT GENERATED] GtkSource.StyleSchemeChooser -[NOT GENERATED] GtkSource.StyleSchemeChooserButton -[NOT GENERATED PARENT] Gtk.Button -[NOT GENERATED PARENT] Gtk.Actionable -[NOT GENERATED PARENT] Gtk.Activatable -[NOT GENERATED PARENT] GtkSource.StyleSchemeChooser -[NOT GENERATED] GtkSource.StyleSchemeChooserInterface -[NOT GENERATED] GtkSource.StyleSchemeChooserWidget -[NOT GENERATED] GtkSource.StyleSchemeManager -[NOT GENERATED] GtkSource.Tag -[NOT GENERATED PARENT] Gtk.TextTag -[NOT GENERATED] GtkSource.ViewGutterPosition +[dependencies.gobject] +package = "gobject-sys" +git = "https://github.com/gtk-rs/gtk-rs-core" ``` -We now have the list of all the non-yet generated items. Quite convenient! You can also see that we have two kinds of not generated items: - -* `[NOT GENERATED]` -* `[NOT GENERATED PARENT]` - -`[NOT GENERATED PARENT]` means that this object lives in a dependency of the current library. We'll come back on how to add them a bit later. - -Let's start by generating one type. Let's update the "generate" array as follows: +In order to make the features of the sys crate available for users of your safe wrapper, you need to add features. Copy the `[features]` part of the Cargo.toml of your sys crate and paste it into the Cargo.toml of the normal crate. The features are supposed to activate the corresponding features of the sys crate, so you need to make some changes. If for example you have the following sys features: ```toml -generate = [ - "GtkSource.Language", -] +[features] +v1_2 = [] +v1_4 = ["v1_2"] +v1_6 = ["v1_4"] +v1_8 = ["v1_6"] +v1_10 = ["v1_8"] +v1_12 = ["v1_10"] +v1_14 = ["v1_12"] +v1_16 = ["v1_14"] +v1_18 = ["v1_16"] +v1_20 = ["v1_18"] +v1_22 = ["v1_20"] +v1_24 = ["v1_22"] +v1_26 = ["v1_24"] +v1_30 = ["v1_26"] +v1_31 = ["v1_30"] +v1_32 = ["v1_31"] +v1_32_4 = ["v1_32"] +v1_34 = ["v1_32_4"] +v1_36_7 = ["v1_34"] +v1_38 = ["v1_36_7"] +v1_42 = ["v1_38"] +v1_44 = ["v1_42"] +v1_46 = ["v1_44"] +v1_48 = ["v1_46"] +v1_50 = ["v1_48"] +v1_52 = ["v1_50"] +dox = [] ``` -Another `gir` run: +You need to change the features in the Cargo.toml of your normal crate to -```sh -gir -o . +```toml +[features] +v1_2 = ["ffi/v1_2"] +v1_4 = ["ffi/v1_4", "v1_2"] +v1_6 = ["ffi/v1_6", "v1_4"] +v1_8 = ["ffi/v1_8", "v1_6"] +v1_10 = ["ffi/v1_10", "v1_8"] +v1_12 = ["ffi/v1_12", "v1_10"] +v1_14 = ["ffi/v1_14", "v1_12"] +v1_16 = ["ffi/v1_16", "v1_14"] +v1_18 = ["ffi/v1_18", "v1_16"] +v1_20 = ["ffi/v1_20", "v1_18"] +v1_22 = ["ffi/v1_22", "v1_20"] +v1_24 = ["ffi/v1_24", "v1_22"] +v1_26 = ["ffi/v1_26", "v1_24"] +v1_30 = ["ffi/v1_30", "v1_26"] +v1_31 = ["ffi/v1_31", "v1_30"] +v1_32 = ["ffi/v1_32", "v1_31"] +v1_32_4 = ["ffi/v1_32_4", "v1_32"] +v1_34 = ["ffi/v1_34", "v1_32_4"] +v1_36_7 = ["ffi/v1_36_7", "v1_34"] +v1_38 = ["ffi/v1_38", "v1_36_7"] +v1_42 = ["ffi/v1_42", "v1_38"] +v1_44 = ["ffi/v1_44", "v1_42"] +v1_46 = ["ffi/v1_46", "v1_44"] +v1_48 = ["ffi/v1_48", "v1_46"] +v1_50 = ["ffi/v1_50", "v1_48"] +v1_52 = ["ffi/v1_52", "v1_50"] +dox = ["ffi/dox"] ``` -(Again, if you do it on another library and it fails and you can't figure out why, don't hesitate to reach us!) - -We now have a `src/auto/language.rs` file. We need to include all `auto` files in our library. To do so, let's update the `src/lib.rs` file as follows: +## The lib.rs file +The lib.rs file will not be replaced when you run gir. All the code that gir will generate for us is going to be in src/auto. We need to include all `auto` files in our library. To do so, let's update the `src/lib.rs` file as follows: ```rust -pub use auto::*; +#![cfg_attr(feature = "dox", feature(doc_cfg))] +pub use auto::*; mod auto; ``` -Let's compile: - -```sh -cargo build -``` - -It completely failed with a lot of errors. Yeay! - -You guessed it, we need to add a few dependencies to make it work. A lot of those errors were about the fact that the `Language` type didn't exist. Which is weird since we generated it, right? Well, if you take a look at the `src/auto/language.rs` file, you'll see this at the top: - -```rust -glib_wrapper! { - pub struct Language(Object); - - match fn { - get_type => || gtk_source_sys::gtk_source_language_get_type(), - } -} -``` - -This macro comes from the `glib` crate. We didn't import it, therefore the Rust compiler can't find it. We'll also need its `sys` part (the case of `glib` is a bit special). - -A second issue is that we didn't import the `sourceview-sys` crate we generated. Gir produces code expecting this crate to be imported as "ffi" (which you can see in the definition of `Language` above), so we need to rename it in the `Cargo.toml` file, too. -Alongside those two (three if we count `glib-sys`!), we'll need both `libc` and `bitflags`. Let's fix all of those issues at once! For that, we need to update the `Cargo.toml`: +## The Gir.toml file +As you certainly guessed, we have to fill our `Gir.toml` file for the normal crate as well. Let's write it: ```toml -[package] -name = "sourceview" -version = "0.1.0" -authors = ["Guillaume Gomez "] - -[dependencies] -libc = "0.2" -bitflags = "1.0" - -[dependencies.ffi] -package = "sourceview-sys" -path = "./sourceview-sys" - -[dependencies.glib] -git = "https://github.com/gtk-rs/glib" - -[dependencies.glib-sys] -git = "https://github.com/gtk-rs/sys" # all gtk-rs sys crates are in the sys repository -``` +[options] +library = "Pango" +version = "1.0" +min_cfg_version = "1.0" +target_path = "." +girs_directories = ["../gir-files"] +work_mode = "normal" +single_version_file = true +generate_safety_asserts = true +deprecate_by_min_version = true -Let's try to rebuild: +generate = [] -```sh -cargo build +manual = [] ``` -It worked! We have generated the `Language` item! I'll let you take a look at the `src/auto/language.rs` file, then we can continue. - -Again, if you encounter any issue at this stage (if the generated code is invalid for example), don't hesitate to reach us so we can give you a hand! - -We'll now generate the `GtkSource.Region` type. Why this one? Well, I don't want to spoil the surprise so just wait for a bit! - -First, we need to add it into the types to generate into our `Gir.toml` file: +Many of these options look familiar from the last chapter but there are also a few new things in here. Let's take a look at them: -```toml -generate = [ - "GtkSource.Language", - "GtkSource.Region", -] -``` +* `work_mode` value is now set to `normal`, it means it'll generate the high-level Rust api instead of the sys-level. +* `generate_safety_asserts` is used to generates checks to ensure that, or any other kind of initialization needed before being able to use the library. +* `deprecate_by_min_version` is used to generate a [Rust "#[deprecated]"](https://doc.rust-lang.org/edition-guide/rust-2018/the-compiler/an-attribute-for-deprecation.html) attribute based on the deprecation information provided by the `.gir` file. +* `generate = []`: this line currently does nothing. We say to [gir] to generate nothing. We'll fill it later on. +* `manual = []`: this line currently does nothing. We can let [gir] know about objects which it does not have to generate code for. -We regenerate: +Let's make a first generation of our high-level Rust API! ```sh gir -o . ``` -We rebuild: +If you take a look at which files and folders were created, you'll see a new "auto" folder inside "src". This folder contains all the generated code. It doesn't contain anything though. Which makes sense since we're generating nothing. -```sh -cargo build -``` - -Everything works, yeay! Now if we take a look at our newly generated `src/auto/region.rs`, we'll see code like this: +Now it's time to introduce you to a whole new [gir] mode: `not_bound`. Let's give it a try: -```rust -//#[cfg(any(feature = "v3_22", feature = "dox"))] -//fn add_subregion(&self, _start: /*Ignored*/>k::TextIter, _end: /*Ignored*/>k::TextIter); - -//#[cfg(any(feature = "v3_22", feature = "dox"))] -//fn get_buffer(&self) -> /*Ignored*/Option; +```console +> gir -o . -m not_bound +[NOT GENERATED] Pango.Glyph +[NOT GENERATED] Pango.GlyphUnit +[NOT GENERATED] Pango.GlyphItem +[NOT GENERATED] Pango.LayoutRun +[NOT GENERATED] Pango.Alignment +[NOT GENERATED] Pango.Font +[NOT GENERATED] Pango.Language +[NOT GENERATED] Pango.Analysis +[NOT GENERATED] Pango.AttrType +[NOT GENERATED] Pango.Attribute +[NOT GENERATED] Pango.Color +[NOT GENERATED] Pango.AttrColor +[NOT GENERATED] Pango.AttrFloat +[NOT GENERATED] Pango.FontDescription +[NOT GENERATED] Pango.AttrFontDesc +[NOT GENERATED] Pango.AttrFontFeatures +[NOT GENERATED] Pango.AttrInt +[NOT GENERATED] Pango.AttrIterator +[NOT GENERATED] Pango.AttrLanguage +[NOT GENERATED] Pango.AttrList +[NOT GENERATED] Pango.Rectangle +[NOT GENERATED] Pango.AttrShape +[NOT GENERATED] Pango.AttrSize +[NOT GENERATED] Pango.AttrString +[NOT GENERATED] Pango.BaselineShift +[NOT GENERATED] Pango.BidiType (deprecated in 1.44) +[NOT GENERATED] Pango.Context +[NOT GENERATED] Pango.Direction +[NOT GENERATED] Pango.Gravity +[NOT GENERATED] Pango.FontMap +[NOT GENERATED] Pango.GravityHint +[NOT GENERATED] Pango.Matrix +[NOT GENERATED] Pango.FontMetrics +[NOT GENERATED] Pango.FontFamily +[NOT GENERATED] Pango.Fontset +[NOT GENERATED] Pango.Coverage +[NOT GENERATED] Pango.CoverageLevel +[NOT GENERATED] Pango.EllipsizeMode +[NOT GENERATED] Pango.FontFace +[NOT GENERATED] Pango.FontMask +[NOT GENERATED] Pango.Stretch +[NOT GENERATED] Pango.Style +[NOT GENERATED] Pango.Variant +[NOT GENERATED] Pango.Weight +[NOT GENERATED] Pango.FontScale +[NOT GENERATED] Pango.FontsetSimple +[NOT GENERATED PARENT] Pango.Fontset +[NOT GENERATED] Pango.GlyphGeometry +[NOT GENERATED] Pango.GlyphVisAttr +[NOT GENERATED] Pango.GlyphInfo +[NOT GENERATED] Pango.Item +[NOT GENERATED] Pango.GlyphString +[NOT GENERATED] Pango.LogAttr +[NOT GENERATED] Pango.GlyphItemIter +[NOT GENERATED] Pango.Script +[NOT GENERATED] Pango.Layout +[NOT GENERATED] Pango.LayoutDeserializeFlags +[NOT GENERATED] Pango.LayoutIter +[NOT GENERATED] Pango.LayoutLine +[NOT GENERATED] Pango.TabArray +[NOT GENERATED] Pango.WrapMode +[NOT GENERATED] Pango.LayoutSerializeFlags +[NOT GENERATED] Pango.LayoutDeserializeError +[NOT GENERATED] Pango.Overline +[NOT GENERATED] Pango.RenderPart +[NOT GENERATED] Pango.Renderer +[NOT GENERATED] Pango.Underline +[NOT GENERATED] Pango.ScriptIter +[NOT GENERATED] Pango.ShapeFlags +[NOT GENERATED] Pango.ShowFlags +[NOT GENERATED] Pango.TabAlign +[NOT GENERATED] Pango.TextTransform +[NOT GENERATED FUNCTION] Pango.attr_allow_breaks_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_background_alpha_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_background_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_baseline_shift_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_break because of Pango.AttrList and Pango.LogAttr +[NOT GENERATED FUNCTION] Pango.attr_fallback_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_family_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_font_scale_new because of Pango.FontScale and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_foreground_alpha_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_foreground_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_gravity_hint_new because of Pango.GravityHint and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_gravity_new because of Pango.Gravity and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_insert_hyphens_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_letter_spacing_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_line_height_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_line_height_new_absolute because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_overline_color_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_overline_new because of Pango.Overline and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_rise_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_scale_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_sentence_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_show_new because of Pango.ShowFlags and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_stretch_new because of Pango.Stretch and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_strikethrough_color_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_strikethrough_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_style_new because of Pango.Style and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_text_transform_new because of Pango.TextTransform and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_underline_color_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_underline_new because of Pango.Underline and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_variant_new because of Pango.Variant and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_weight_new because of Pango.Weight and Pango.Attribute +[NOT GENERATED FUNCTION] Pango.attr_word_new because of Pango.Attribute +[NOT GENERATED FUNCTION] Pango.break (deprecated in 1.44) because of Pango.Analysis and Pango.LogAttr +[NOT GENERATED FUNCTION] Pango.default_break because of Pango.Analysis and Pango.LogAttr +[NOT GENERATED FUNCTION] Pango.extents_to_pixels because of Pango.Rectangle and Pango.Rectangle +[NOT GENERATED FUNCTION] Pango.find_base_dir because of Pango.Direction +[NOT GENERATED FUNCTION] Pango.get_log_attrs because of Pango.Language and Pango.LogAttr +[NOT GENERATED FUNCTION] Pango.itemize because of Pango.Context, Pango.AttrList, Pango.AttrIterator and Pango.Item +[NOT GENERATED FUNCTION] Pango.itemize_with_base_dir because of Pango.Context, Pango.Direction, Pango.AttrList, Pango.AttrIterator and Pango.Item +[NOT GENERATED FUNCTION] Pango.log2vis_get_embedding_levels because of Pango.Direction +[NOT GENERATED FUNCTION] Pango.markup_parser_finish because of GLib.MarkupParseContext, Pango.AttrList and GLib.Error +[NOT GENERATED FUNCTION] Pango.markup_parser_new because of GLib.MarkupParseContext +[NOT GENERATED FUNCTION] Pango.parse_markup because of Pango.AttrList and GLib.Error +[NOT GENERATED FUNCTION] Pango.parse_stretch because of Pango.Stretch +[NOT GENERATED FUNCTION] Pango.parse_style because of Pango.Style +[NOT GENERATED FUNCTION] Pango.parse_variant because of Pango.Variant +[NOT GENERATED FUNCTION] Pango.parse_weight because of Pango.Weight +[NOT GENERATED FUNCTION] Pango.read_line (deprecated in 1.38) because of GLib.String +[NOT GENERATED FUNCTION] Pango.reorder_items because of Pango.Item and Pango.Item +[NOT GENERATED FUNCTION] Pango.scan_string (deprecated in 1.38) because of GLib.String +[NOT GENERATED FUNCTION] Pango.scan_word (deprecated in 1.38) because of GLib.String +[NOT GENERATED FUNCTION] Pango.shape because of Pango.Analysis and Pango.GlyphString +[NOT GENERATED FUNCTION] Pango.shape_full because of Pango.Analysis and Pango.GlyphString +[NOT GENERATED FUNCTION] Pango.shape_item because of Pango.Item, Pango.LogAttr, Pango.GlyphString and Pango.ShapeFlags +[NOT GENERATED FUNCTION] Pango.shape_with_flags because of Pango.Analysis, Pango.GlyphString and Pango.ShapeFlags +[NOT GENERATED FUNCTION] Pango.tailor_break because of Pango.Analysis and Pango.LogAttr +[NOT GENERATED FUNCTION] Pango.unichar_direction because of Pango.Direction ``` -Some functions are commented. Why so? The reason is simple: we need to tell to `gir` that those types have been generated and that it can generate code using them. We can do it by adding the type into the "manual" list. To put it simply, when [gir] sees an item into this "manual" list, it means to it "this type has been generated somewhere else, you can use it just like the others". - -Let's update our `Gir.toml` file once again: +We now have the list of all the not-yet generated items. Quite convenient! There can be different kinds of not generated items: -```toml -generate = [ - "GtkSource.Language", - "GtkSource.Region", -] - -manual = [ - "Gtk.TextIter", - "Gtk.TextBuffer", -] +* `[NOT GENERATED]`: +Objects marked with `[NOT GENERATED]` are objects that we can generate, but we did not (yet) add to the `generate` array. +* `[NOT GENERATED PARENT]`: +These objects live in a dependency of the current library. These are the objects we will add to the `manual` array in the following steps. +* `[NOT GENERATED FUNCTION]`: +These are global functions that were not generated. To fix it, we just add `"NameOfYourLibrary.*"` to the `generate` array in the Git.toml and add the following line to your src/lib.rs file: +```rust +pub use auto::functions::*; ``` -We'll also need to import the `gtk` crate. Let's add it into our `Cargo.toml` file: - -```toml -[dependencies.gtk] -git = "https://github.com/gtk-rs/gtk" -``` +## Generating the code +In order to generate the code for the safe wrapper, we follow these steps until all objects have been generated: -We regenerate and rebuild: +- Run `gir -o . -m not_bound` to see which objects have not been generated yet +- Pick one of the types marked with `[NOT GENERATED]` +- Add it to the `generate` array in the Gir.toml file +- Run `gir -o .` to generate the code +- Open the generated files under src/auto and have a look at them +- Search for `/*Ignored*/`. If the type name following `/*Ignored*/` is prepended by `[crate_name]::` (e.g `Ignored*/&glib::MarkupParseContext`), + - then we add it to the `manual` array. By doing so we tell [gir] that those types have been generated somewhere else and that they can be used just like the other types. + - Otherwise, the type comes from the current crate and we just put it into the `generate` list of the `Gir.toml` file. +- Start with the first step again + +The names of the objects are not the same as the crates names. You have to use the names of the corresponding gir files. -```sh -gir -o . -cargo build -``` +Okay, lets go through that process for a few objects of our example. -Everything is working, yeay! If you take another look at `src/auto/region.rs`, you'll see a lot less commented functions. Amongst the remaining ones, you'll see this one: +🚧 TODO: Add remaining steps of the pango example 🚧 -```rust -//#[cfg(any(feature = "v3_22", feature = "dox"))] -//fn get_start_region_iter(&self, iter: /*Ignored*/RegionIter); -``` +Again, if you do it on another library and it fails and you can't figure out why, don't hesitate to [contact us](https://gtk-rs.org/contact)! -If a type name isn't prepend by `[crate_name]::`, then it means it comes from the current crate. To add it, just put it into the "generate" list of `Gir.toml`. +At this point, you should have almost everything you need. Let's have a look at errors that can happen in this process. -At this point, you should have almost everything you need. There is just one last case we need to talk about. [gir]: https://github.com/gtk-rs/gir diff --git a/book/src/tutorial/introduction.md b/book/src/tutorial/introduction.md index 49aace643..4b149a697 100644 --- a/book/src/tutorial/introduction.md +++ b/book/src/tutorial/introduction.md @@ -1,15 +1,6 @@ # Tutorial +In this tutorial, we go through the basic steps needed to generate a safe wrapper for a simple C library. We start with finding the .gir files needed and end with generating the documentation. We also look at a few common errors and how to fix them. Because this can be a bit abstract sometimes, we use the creation of the bindings for pango as an example. The example continues through all of the chapters of the tutorial. If you follow along until the end, you will have generated a safe wrapper including the documentation. In case you are stuck at any point or there are other errors and you can't figure out what's going on, don't hesitate to reach us so we can give you a hand! -Let's see how to generate Rust bindings of a GNOME library using the [gir] crate. - -Note that the `.gir` files often happens that they are invalid (missing or invalid annotations for example). We have a small script to fix the `.gir` files we're using (and only them!) available in the [gir-files repository](https://github.com/gtk-rs/gir-files/blob/master/fix.sh). You can run it like this (at the same level of the `.gir` files you want to patch): - -```sh -sh fix.sh -``` - -All `gtk-rs` generated crates come in two parts: the `sys` part which contains all the C functions and types definitions (direct mapping, everything is unsafe) and the "high-level" part which contains the nice, completely safe and idiomatic Rust API on top of the `sys` part. - -As an example, we'll generate the `sourceview` library bindings. So first, let's generate the `sys` part! +Let's dive right into it and let's set up our project folder! [gir]: https://github.com/gtk-rs/gir diff --git a/book/src/tutorial/preparation.md b/book/src/tutorial/preparation.md new file mode 100644 index 000000000..00127d1bd --- /dev/null +++ b/book/src/tutorial/preparation.md @@ -0,0 +1,67 @@ +# Preparation +In order to install gir and nicely structure the project, there are a few things to set up. + +## Set up the project folder +In order to keep the project folder nicely organized, lets create a folder where we will work in and initialize the repo. We will create two library crates. pango will contain the safe wrapper crate and because it is a wrapper for the unsafe bindings, we create the pango-sys crate within the pango crate. If no Cargo.toml file is present in the sys create, a new one will be generated, so lets be safe and delete the automatically created file before we begin. The following commands will set up the project folder as described. +```sh +mkdir gir-tutorial +cd gir-tutorial/ +git init +cargo new pango --lib +cd pango +cargo new pango-sys --lib +rm pango-sys/Cargo.toml +``` +We will also create a file called "Gir.toml" in each of the crates. +```sh +touch Gir.toml +touch pango-sys/Gir.toml +cd .. +``` + +## Installing gir +Of course we also need to download and install [gir]. +```sh +git submodule add https://github.com/gtk-rs/gir +cd gir +cargo install --path . +cd .. +``` +By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. + +If there are any updates to gir in the future, we can install them by opening our project folder `gir-tutorial` and running +```sh +git submodule update --remote +cd gir +cargo install --path . +cd .. +``` + +## Summary +You should now have a folder looking like this: +```text +gir + | + |---- ... +pango/ + | + |---- Cargo.toml + |---- Gir.toml + |---- pango-sys/ + | | + | |---- Gir.toml + | |---- src/ + | | + | |---- lib.rs + |---- src/ + | + |---- lib.rs +.git + | + |---- ... +.gitmodules +``` + +Now that we installed gir and prepared our project folder, let's get the .gir files. + +[gir]: https://github.com/gtk-rs/gir diff --git a/book/src/tutorial/sys_library.md b/book/src/tutorial/sys_library.md index 87d2842b5..d375315c5 100644 --- a/book/src/tutorial/sys_library.md +++ b/book/src/tutorial/sys_library.md @@ -1,81 +1,45 @@ # Generating the FFI library - -First, you'll need to download [gir]: - -```sh -git clone https://github.com/gtk-rs/gir -cd gir -cargo install --path . # so we can use gir binary directly -``` - -Then the `.gir` files (luckily for you, we have a repository which contains all the ones you need for sourceview!): - -```sh -git clone https://github.com/gtk-rs/gir-files -``` - -If you look into `gir-files`, you'll see a file named `GtkSource-3.0.gir`. That's the one for sourceview. - -Now let's create a new project for our `sourceview` crate: - -```sh -cargo new sourceview --lib -``` - -Then let's create a folder inside the newly created `sourceview` folder for the `sys` part: +We have installed gir, set up our repo and have all the .gir files we need. Now let's work on the unsafe bindings of the -sys crate. Let's change into the directory of the sys crate. ```sh -cd sourceview -cargo new sourceview-sys --lib +cd pango/pango-sys ``` -To indicate to [gir] what to generate, we'll need a `Gir.toml` file (inside the `sourceview-sys` folder) containing: +## The Gir.toml file +The first step is to let [gir] know what to generate. This is what the `Gir.toml` file that we created inside the `pango-sys` folder is for. This file will not be replaced when we run gir again. The file is currently empty so let's add the following to it: ```toml [options] -library = "GtkSource" -version = "3.0" +library = "Pango" +version = "1.0" +min_cfg_version = "1.0" target_path = "." -min_cfg_version = "3.0" +girs_directories = ["../../gir-files/"] +work_mode = "sys" +single_version_file = true ``` -* `library` stands for the library we want to generate. -* `version` stands for the version of the library to be used. -* `target_path` stands for the location where the files will be generated. +* `library` stands for the name of the library we want to generate. +* `version` stands for the version of the library to be used. * `min_cfg_version` will be the minimum version supported by the generated bindings. +* `target_path` stands for the location where the files will be generated. +* `girs_directories` stands for the location of the `.gir` files. +* `work_mode` stands for the mode gir is using. The options here are `sys` and `normal`. +* `single_version_file` is a very useful option when you have a lot of generated files (like we'll have). Instead of generating the gir hash commit used for the generation in the header of all generated files, it'll just write it inside one file, removing `git diff` noise **a lot**. -You should now have a folder looking like this: - -```text -sourceview/ - | - |---- Cargo.toml - |---- sourceview-sys/ - | | - | |---- Cargo.toml - | |---- Gir.toml - | |---- src/ - | | - | |---- lib.rs - |---- src/ - | - |---- lib.rs -``` +You can find out the values for `library` and `version` by looking at the name of the .gir file of your library. In our case it is called Pango-1.0.gir. This tells us that the `library` is Pango and the `version` is 1.0. If you don't know what value to use for `min_cfg_version`, use the same as you use for `version`. Because we are generating the unsafe bindings, we use the `sys` work mode. Let's generate the `sys` crate now: ```sh -cd sourceview-sys -# Run gir in "sys" mode (the "-m" option). We give the gir files path (the "-d" option) and output path (the "-o" option). -gir -m sys -d ../../gir-files/ -o . +gir -o . ``` -(In case a failure happens at this point, and you can't figure out what's going on, don't hesitate to reach us so we can give you a hand!) - -You should now see new files (and a new folder): +You should now see new files and a new folder. * `build.rs` * `Cargo.toml` +* `Gir.toml` * `src/lib.rs` * `tests/` @@ -85,72 +49,48 @@ Now let's try to build it: cargo build ``` -Surprise! It doesn't build at all and you should see a loooooot of errors. Well, that was expected. We need to add some dependencies (you can find which ones in the `.gir` files) in order to make it work. Let's update our `Gir.toml` file to make it look like this: - -```toml -[options] -library = "GtkSource" -version = "3.0" -target_path = "." -min_cfg_version = "3.0" - -external_libraries = [ - "Cairo", - "Gdk", - "GdkPixbuf", - "Gio", - "GLib", - "GObject", - "Gtk", -] -``` - -Now we regenerate it then rebuild it: - -```sh -rm Cargo.* # we remove Cargo files -gir -m sys -d ../../gir-files/ -o . -cargo build +Surprise! It doesn't build at all and you should see a loooooot of errors. Well, that was expected. We need to add some dependencies in order to make it work. Have a look at the errors of the compiler to find out which are missing. In our example, the compiler throws the following errors: +```rust +use of undeclared crate or module `glib` +use of undeclared crate or module `gobject` ``` - -Should work just fine! - -We can cleanup the command line a bit now. You can actually give the work mode ("-m" option) and the gir files repository through the `Gir.toml` file using "work_mode" and "girs_directories" options: +The dependencies need to be added to the `external_libraries` part. The names of the dependencies are the same as in the .gir files and not what the packages to install the libraries might be called. The compiler told us that the `GLib` and the `GObject` dependencies are missing. Let's update our `Gir.toml` file to fix it: ```toml [options] -library = "GtkSource" -version = "3.0" +library = "Pango" +version = "1.0" +min_cfg_version = "1.0" target_path = "." -min_cfg_version = "3.0" -work_mode = "sys" girs_directories = ["../../gir-files/"] +work_mode = "sys" external_libraries = [ - "Cairo", - "Gdk", - "GdkPixbuf", - "Gio", "GLib", "GObject", - "Gtk", ] ``` -Now, if you want to regenerate, just run: - + If one of your .gir files changed or you want to use an updated version of gir to generate the code, there is no need to delete the Cargo.toml or the Cargo.lock files before you regenerate the code. Because we made some changes to the Gir.toml file, we have to run gir again. Changing the content of the external_libraries array means that additional dependencies have to be added. gir does this automatically for you, but only if there is no Cargo.toml and no Cargo.lock file present. Just remove the `Cargo.*` files and run gir again and the additional dependencies will be added. If you made any manual changes to the file, you would have to do these changes again. After regenerating the code, we build the crate to see if the errors are gone. ```sh +rm Cargo.* gir -o . +cargo build ``` - -Now we have a working `sys` containing all functions and objects definition. Just to be sure everything was correctly generated, we can run some tests (graciously generated by [gir] as well): - +When executing the above commands, there should not be any errors and everything should work fine. Just to be sure everything was correctly generated, we can run some tests (graciously generated by [gir] as well): ```sh cargo test ``` - Normally, all tests passed. If you get an error when running those tests, it's very likely that the `sys` generation is invalid and/or incomplete. +## The Gir.toml file +This file was automatically generated but it will not be replaced when we run gir again. Make sure to look at your Cargo.toml to optionally add more information to it. If there are any `[features]`, you should try building and testing with these features activated as well. If you'd like, you can also set the default features. This can be useful for example if you want to always activate the newest version unless the user of the crate specifies an older version. + +For our example, we now have a working `sys` crate containing all functions and objects definition. We are done here and can go back to the folder of the wrapper crate: +```sh +cd .. +``` Time to generate the high-level Rust API! + [gir]: https://github.com/gtk-rs/gir From f8a13a70aa9890fa8bc00a30ee6fd88320cb4c8e Mon Sep 17 00:00:00 2001 From: pentamassiv <91755244+pentamassiv@users.noreply.github.com> Date: Tue, 1 Nov 2022 21:33:01 +0100 Subject: [PATCH 2/9] Applied Bilals suggestions Co-authored-by: Bilal Elmoussaoui Co-authored-by: Bilal Elmoussaoui Co-authored-by: Bilal Elmoussaoui Co-authored-by: Bilal Elmoussaoui Co-authored-by: Bilal Elmoussaoui Co-authored-by: Bilal Elmoussaoui Co-authored-by: Bilal Elmoussaoui Co-authored-by: Bilal Elmoussaoui --- book/src/introduction.md | 10 +++++----- book/src/tutorial/introduction.md | 2 +- book/src/tutorial/sys_library.md | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/book/src/introduction.md b/book/src/introduction.md index 78d11c916..b470e75a6 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -1,13 +1,13 @@ # Introduction -[gir] is a tool to automatically generate bindings and a safe wrapper for a library written in C. All you need to be able to use it, is a `.gir` file containing the [GObject introspection](https://gi.readthedocs.io/en/latest/) data for the library you want to create the bindings for, as well as the `.gir` files for all its dependencies. Have a look at the tutorial if you don't know how to [find the .gir files](tutorial/finding_gir_files.md). If your library does not provide a `.gir` file, unfortunately you cannot use [gir], but maybe you can try [rust-bindgen](https://github.com/rust-lang/rust-bindgen). +[gir] is a tool to automatically generate FFI bindings and a safe wrapper for a library written in C. All you need to be able to use it, is a `.gir` file containing the [GObject introspection](https://gi.readthedocs.io/en/latest/) data for the library you want to create the bindings for, as well as the `.gir` files for all its dependencies. Have a look at the tutorial if you don't know how to [find the .gir files](tutorial/finding_gir_files.md). If your library does not provide a `.gir` file, unfortunately you cannot use [gir], but maybe you can try [rust-bindgen](https://github.com/rust-lang/rust-bindgen). -This book contains a tutorial on how to use gir. As an example we will create the bindings for pango. In many cases you will be able to follow the same steps with your library. If you are already familiar with gir and you just want to look up details about the configuration files, feel free to skip ahead to the documentation of the [configuration files](config/introduction.md). +This book contains a tutorial on how to use [gir]. As an example we will create the bindings for [Pango](https://docs.gtk.org/Pango/). In many cases you will be able to follow the same steps with your library. If you are already familiar with [gir] and you just want to look up details about the configuration files, feel free to skip ahead to the documentation of the [configuration files](config/introduction.md). ## General steps [gir] tries to make it as simple as possible to generate a safe wrapper for your C library. The process can be divided into four steps that correspond to the four operating modes gir has. - Generating unsafe bindings: -In this step, the low-level FFI bindings are created from the supplied `*.gir` file. These are essentially direct calls into the related C library and are typically unsafe. The resulting crate is typically appended with -sys. The operating mode is `sys`. +In this step, the low-level FFI bindings are created from the supplied `*.gir` files. These are essentially direct calls into the related C library and are unsafe. The resulting crate is typically appended with -sys. The operating mode is `sys`. - Generating a safe wrapper: Next, another crate for a layer on top of these unsafe (sys) bindings is created, which makes them safe for use in general Rust. The operating mode is `normal`. @@ -20,12 +20,12 @@ After the safe wrapper is created, gir can even generate the documentation for u ## Regenerating the bindings and wrapper -In order to generate the bindings and the wrapper for the first time, the above mentioned steps should be followed. When you want to regenerate the crates because e.g. the library was updated, you can simplify the process by running the helper script `./generator.py`. The script detects `Gir.toml` configurations in the current directory (or the paths passed on the command-line) and generates "normal" or "sys" crates for it. Alternatively --embed-docs can be passed to prepare source-code for a documentation built by moving all documentation into it. For a complete overview of available options, pass --help. +In order to generate the bindings and the wrapper for the first time, the above mentioned steps should be followed. When you want to regenerate the crates because e.g. the library was updated, you can simplify the process by running the helper script `./generator.py`. The script detects `Gir.toml` configurations in the current directory and sub-directories (or the paths passed on the command-line) and generates "normal" or "sys" crates for it. Alternatively `--embed-docs` can be passed to prepare source-code for a documentation built by moving all documentation into it. For a complete overview of available options, pass `--help`. ## GIR format reference It can always be useful to look at the [reference](https://gi.readthedocs.io/en/latest/annotations/giannotations.html) or [schema](https://gitlab.gnome.org/GNOME/gobject-introspection/blob/master/docs/gir-1.2.rnc). ## Contact us -If you use gir on another library and it fails and you can't figure out why, don't hesitate to [contact us](https://gtk-rs.org/contact)! +If you use [gir] on another library and it fails and you can't figure out why, don't hesitate to [contact us](https://gtk-rs.org/contact)! [gir]: https://github.com/gtk-rs/gir \ No newline at end of file diff --git a/book/src/tutorial/introduction.md b/book/src/tutorial/introduction.md index 4b149a697..302d0913c 100644 --- a/book/src/tutorial/introduction.md +++ b/book/src/tutorial/introduction.md @@ -1,5 +1,5 @@ # Tutorial -In this tutorial, we go through the basic steps needed to generate a safe wrapper for a simple C library. We start with finding the .gir files needed and end with generating the documentation. We also look at a few common errors and how to fix them. Because this can be a bit abstract sometimes, we use the creation of the bindings for pango as an example. The example continues through all of the chapters of the tutorial. If you follow along until the end, you will have generated a safe wrapper including the documentation. In case you are stuck at any point or there are other errors and you can't figure out what's going on, don't hesitate to reach us so we can give you a hand! +In this tutorial, we go through the basic steps needed to generate a safe wrapper for a simple C library. We start with finding the .gir files needed and end with generating the documentation. We also look at a few common errors and how to fix them. Because this can be a bit abstract sometimes, we use the creation of the bindings for [Pango](https://docs.gtk.org/Pango/) as an example. The example continues through all of the chapters of the tutorial. If you follow along until the end, you will have generated a safe wrapper including the documentation. In case you are stuck at any point or there are other errors and you can't figure out what's going on, don't hesitate to reach us so we can give you a hand! Let's dive right into it and let's set up our project folder! diff --git a/book/src/tutorial/sys_library.md b/book/src/tutorial/sys_library.md index d375315c5..18fce4fb4 100644 --- a/book/src/tutorial/sys_library.md +++ b/book/src/tutorial/sys_library.md @@ -1,5 +1,5 @@ # Generating the FFI library -We have installed gir, set up our repo and have all the .gir files we need. Now let's work on the unsafe bindings of the -sys crate. Let's change into the directory of the sys crate. +We have installed [gir], set up our repo and have all the `.gir` files we need. Now let's work on the unsafe bindings of the -sys crate. Let's change into the directory of the sys crate. ```sh cd pango/pango-sys @@ -49,7 +49,7 @@ Now let's try to build it: cargo build ``` -Surprise! It doesn't build at all and you should see a loooooot of errors. Well, that was expected. We need to add some dependencies in order to make it work. Have a look at the errors of the compiler to find out which are missing. In our example, the compiler throws the following errors: +Surprise! It doesn't build at all and you should see a lot of errors. Well, that was expected. We need to add some dependencies in order to make it work. Have a look at the errors of the compiler to find out which are missing. In our example, the compiler throws the following errors: ```rust use of undeclared crate or module `glib` use of undeclared crate or module `gobject` From 7991951975781135c09330dbc6a96a20dee4eb7c Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Tue, 1 Nov 2022 23:24:20 +0100 Subject: [PATCH 3/9] Add information about the dox feature --- book/src/tutorial/generate_docs.md | 4 +++- book/src/tutorial/high_level_rust_api.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/book/src/tutorial/generate_docs.md b/book/src/tutorial/generate_docs.md index 27595281e..1e7fc06cf 100644 --- a/book/src/tutorial/generate_docs.md +++ b/book/src/tutorial/generate_docs.md @@ -33,4 +33,6 @@ cargo doc Congratulations, we are done! You have successfully created the safe wrapper for a C library! -You can easily publish your generated bindings and the wrapper to crates.io to allow others to use it. Publishing crates is easy but keep in mind that they need to be maintained as well. We set up the project folder in a way that easily allows sharing the code. All that is needed is to add some information to your Cargo.toml. Gir will not override them when you re-generate bindings. Easy, right? If this is your first time publishing a crate, you can find a detailed guide [here](https://doc.rust-lang.org/cargo/reference/publishing.html). \ No newline at end of file +You can easily publish your generated bindings and the wrapper to crates.io to allow others to use it. Publishing crates is easy but keep in mind that they need to be maintained as well. We set up the project folder in a way that easily allows sharing the code. All that is needed is to add some information to your Cargo.toml. Gir will not override them when you re-generate bindings. Easy, right? If this is your first time publishing a crate, you can find a detailed guide [here](https://doc.rust-lang.org/cargo/reference/publishing.html). + +Before you publish the crate, please ensure docs.rs will activate the dox feature and the dox feature of the safe wrapper crate also activates the feature of its dependencies and the unsafe FFI bindings you created. Feel free to go back to the chapter about the [Cargo.toml file of the safe wrapper](high_level_rust_api.md#the-cargotoml-file) to read more about it. If you skip this step, your crate and all crates depending on it will not have documentation available on docs.rs. \ No newline at end of file diff --git a/book/src/tutorial/high_level_rust_api.md b/book/src/tutorial/high_level_rust_api.md index a733a0651..70c08dae6 100644 --- a/book/src/tutorial/high_level_rust_api.md +++ b/book/src/tutorial/high_level_rust_api.md @@ -9,7 +9,7 @@ Add the following lines to the file: [package.metadata.docs.rs] features = ["dox"] ``` -This automatically activates the `dox` feature if you chose to publish the bindings and docs.rs tries to build the documentation. If you are not going to maintain the crate and don't want to publish it, this line is not going to hurt. +This automatically activates the `dox` feature if you chose to publish the bindings and docs.rs tries to build the documentation. The `dox` feature skips linking the C libraries and allows docs.rs to build the documentation without having the underlying libraries installed. Even if you don't plan to publish it, this line is not going to hurt. We also need to add `libc`, `bitflags`, `glib` and `glib-sys` and all other dependencies we used in the sys crate as dependencies. Because we are creating a wrapper for the sys crate, which we generated in the previous chapter, we also need to add the sys crate to the list of dependencies. In the automatically generated code, the sys crate is always called `ffi`, so we need to rename the sys crate in our `Cargo.toml`. For our example, this results in the following dependencies: ```toml From 7c08952d0c31c814cd5b884434e1023cfb5ef4c5 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Wed, 2 Nov 2022 00:39:17 +0100 Subject: [PATCH 4/9] Don't make the submodules auto update --- book/src/tutorial/finding_gir_files.md | 4 ++-- book/src/tutorial/preparation.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/book/src/tutorial/finding_gir_files.md b/book/src/tutorial/finding_gir_files.md index 4215e46f1..7e81f9ca4 100644 --- a/book/src/tutorial/finding_gir_files.md +++ b/book/src/tutorial/finding_gir_files.md @@ -21,6 +21,6 @@ We want to generate the wrapper for pango. It is related to GTK, so in order to ```sh git clone --depth 1 https://github.com/gtk-rs/gir-files +git config -f .gitmodules submodule.gir-files.update none ``` - -If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. That's the one for pango. Because we already cloned the gir-files repo, we also have all the other .gir files of the dependencies that we need. Now we can create the unsafe bindings. \ No newline at end of file +We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. That's the one for pango. Because we already cloned the gir-files repo, we also have all the other .gir files of the dependencies that we need. Now we can create the unsafe bindings. \ No newline at end of file diff --git a/book/src/tutorial/preparation.md b/book/src/tutorial/preparation.md index 00127d1bd..69b6e153b 100644 --- a/book/src/tutorial/preparation.md +++ b/book/src/tutorial/preparation.md @@ -23,11 +23,12 @@ cd .. Of course we also need to download and install [gir]. ```sh git submodule add https://github.com/gtk-rs/gir +git config -f .gitmodules submodule.gir.update none cd gir cargo install --path . cd .. ``` -By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. +By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. If there are any updates to gir in the future, we can install them by opening our project folder `gir-tutorial` and running ```sh From 03e269808cfed842204a709abee3bf4cdc3b7c87 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Nov 2022 13:56:43 +0100 Subject: [PATCH 5/9] Add gir-files as submodule --- book/src/tutorial/finding_gir_files.md | 16 ++++++++-------- book/src/tutorial/preparation.md | 4 ++-- book/src/tutorial/sys_library.md | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/book/src/tutorial/finding_gir_files.md b/book/src/tutorial/finding_gir_files.md index 7e81f9ca4..b1b4737ec 100644 --- a/book/src/tutorial/finding_gir_files.md +++ b/book/src/tutorial/finding_gir_files.md @@ -1,26 +1,26 @@ # Where can I find those .gir files? -There are multiple ways you can get the needed `.gir` files. The `*.gir` you need corresponds to the library you want to generate bindings for. If you have the library installed, you can search for the `.gir` file under `/usr/share/gir-1.0/`. Having the library installed is a good idea so that you can test the generated code. Otherwise you should be able to get it from the package that installs the library. Ubuntu for example allows you to [search](https://packages.ubuntu.com/) for packages and download them via their website. +There are multiple ways you can get the needed `.gir` files. The `*.gir` you need corresponds to the library you want to generate bindings for. If you have the library installed, you can search for the `.gir` file under `/usr/share/gir-1.0/`. Having the library installed is a good idea so that you can test the generated code. Otherwise you should be able to get it from the package that installs the library. Ubuntu for example allows you to [search](https://packages.ubuntu.com/) for packages and download them via their website. You can copy the `.gir` file of your library to the root of your project folder. You don't have to store all `.gir` files in the same folder. You can add multiple paths by changing the `girs_directories` field in the Gir.toml files. More on this in the next chapters. -Have a look at the .gir file of your library. At the beginning of the file, you probably see something similar to ``. "GObject" in this case would be a dependency and you will have to find the .gir file for your dependencies as well. +Have a look at the .gir file of your library. At the beginning of the file, you probably see something similar to ``. "GObject" in this case would be a dependency and you will have to find the .gir file for your dependencies as well. In most cases it will be enough to follow the next two steps of the tutorial to get all needed files. ## GTK dependencies -If your library depends on GTK libraries, the recommended way to get the `.gir` files for them is to clone the [gir-files repo](https://github.com/gtk-rs/gir-files). It's the recommended way, because some of the `.gir` files included in the libraries are invalid (missing or invalid annotations for example). These errors are already fixed in the gir files from the repo. Otherwise you could use the above mentioned methods to find the files and run the [script](https://github.com/gtk-rs/gir-files/blob/master/fix.sh) to fix the `.gir` files available in the gir-files repository (and only them!). You can run it like this (at the same level of the `.gir` files you want to patch): +If your library depends on GTK libraries, the recommended way to get the `.gir` files for them is to add the [gir-files repo](https://github.com/gtk-rs/gir-files) as a submodule as well. It's the recommended way, because some of the `.gir` files included in the libraries are invalid (missing or invalid annotations for example). These errors are already fixed in the gir files from the repo. Otherwise you could use the above mentioned methods to find the files and run the [script](https://github.com/gtk-rs/gir-files/blob/master/fix.sh) to fix the `.gir` files available in the gir-files repository (and only them!). You can run it like this (at the same level of the `.gir` files you want to patch): ```sh sh fix.sh ``` ## GStreamer dependencies -For GStreamer related dependencies, follow the above mentioned steps but clone this [repo](https://gitlab.freedesktop.org/gstreamer/gir-files-rs) instead. +For GStreamer related dependencies, follow the above mentioned steps but add this [repo](https://gitlab.freedesktop.org/gstreamer/gir-files-rs) instead. ## Other dependencies If you have other dependencies, you have to find the files yourself. They can often be found in the repo containing the source of your dependencies or if you have them installed, you might find them under `/usr/share/gir-1.0/` again. ## Example -We want to generate the wrapper for pango. It is related to GTK, so in order to get its .gir files, we use the recommended way. While being in the project folder `git-tutorial`, we clone the [gir-files repo](https://github.com/gtk-rs/gir-files). +We want to generate the wrapper for pango. It is related to GTK, so in order to get its .gir files, we use the recommended way. While being in the project folder `git-tutorial`, we add the [gir-files repo](https://github.com/gtk-rs/gir-files) as a submodule and set the branch of the submodule to master. ```sh -git clone --depth 1 https://github.com/gtk-rs/gir-files -git config -f .gitmodules submodule.gir-files.update none +git submodule add https://github.com/gtk-rs/gir-files +git submodule set-branch --branch master -- ./gir-files ``` -We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. That's the one for pango. Because we already cloned the gir-files repo, we also have all the other .gir files of the dependencies that we need. Now we can create the unsafe bindings. \ No newline at end of file +If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. That's the one for pango. Because we already added the gir-files repo, we also have all the other .gir files of the dependencies that we need. Now we can create the unsafe bindings. \ No newline at end of file diff --git a/book/src/tutorial/preparation.md b/book/src/tutorial/preparation.md index 69b6e153b..e19fe29b2 100644 --- a/book/src/tutorial/preparation.md +++ b/book/src/tutorial/preparation.md @@ -23,12 +23,12 @@ cd .. Of course we also need to download and install [gir]. ```sh git submodule add https://github.com/gtk-rs/gir -git config -f .gitmodules submodule.gir.update none +git submodule set-branch --branch master -- ./gir cd gir cargo install --path . cd .. ``` -By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. +By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. We also set the branch of the submodule to master. If there are any updates to gir in the future, we can install them by opening our project folder `gir-tutorial` and running ```sh diff --git a/book/src/tutorial/sys_library.md b/book/src/tutorial/sys_library.md index 18fce4fb4..e7865f1a8 100644 --- a/book/src/tutorial/sys_library.md +++ b/book/src/tutorial/sys_library.md @@ -27,7 +27,7 @@ single_version_file = true * `work_mode` stands for the mode gir is using. The options here are `sys` and `normal`. * `single_version_file` is a very useful option when you have a lot of generated files (like we'll have). Instead of generating the gir hash commit used for the generation in the header of all generated files, it'll just write it inside one file, removing `git diff` noise **a lot**. -You can find out the values for `library` and `version` by looking at the name of the .gir file of your library. In our case it is called Pango-1.0.gir. This tells us that the `library` is Pango and the `version` is 1.0. If you don't know what value to use for `min_cfg_version`, use the same as you use for `version`. Because we are generating the unsafe bindings, we use the `sys` work mode. +You can find out the values for `library` and `version` by looking at the name of the .gir file of your library. In our case it is called Pango-1.0.gir. This tells us that the `library` is Pango and the `version` is 1.0. If you don't know what value to use for `min_cfg_version`, use the same as you use for `version`. If not all `.gir` files that are needed reside in `../../gir-files/`, you can add the path to the other files by changing `girs_directories`. If for example you also have `.gir` files in the root of your project folder, change it to `girs_directories = ["../../gir-files/", "../.."]`. Because we are generating the unsafe bindings, we use the `sys` work mode. Let's generate the `sys` crate now: From 2751e47c3b4980d168c95d739bafe7ad17f26ec3 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Nov 2022 14:00:04 +0100 Subject: [PATCH 6/9] Fix dox feature in example --- book/src/tutorial/high_level_rust_api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/src/tutorial/high_level_rust_api.md b/book/src/tutorial/high_level_rust_api.md index 70c08dae6..8e6c2732b 100644 --- a/book/src/tutorial/high_level_rust_api.md +++ b/book/src/tutorial/high_level_rust_api.md @@ -60,7 +60,7 @@ v1_46 = ["v1_44"] v1_48 = ["v1_46"] v1_50 = ["v1_48"] v1_52 = ["v1_50"] -dox = [] +dox = ["glib/dox", "gobject/dox"] ``` You need to change the features in the Cargo.toml of your normal crate to @@ -93,7 +93,7 @@ v1_46 = ["ffi/v1_46", "v1_44"] v1_48 = ["ffi/v1_48", "v1_46"] v1_50 = ["ffi/v1_50", "v1_48"] v1_52 = ["ffi/v1_52", "v1_50"] -dox = ["ffi/dox"] +dox = ["glib/dox", "gobject/dox", "ffi/dox"] ``` ## The lib.rs file From c8dc3f045fcb29875987f01c0cb842b9f91c01a6 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 6 Nov 2022 14:11:18 +0100 Subject: [PATCH 7/9] Don't make the submodules auto update --- book/src/tutorial/finding_gir_files.md | 3 ++- book/src/tutorial/preparation.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/book/src/tutorial/finding_gir_files.md b/book/src/tutorial/finding_gir_files.md index b1b4737ec..5f23e8fea 100644 --- a/book/src/tutorial/finding_gir_files.md +++ b/book/src/tutorial/finding_gir_files.md @@ -21,6 +21,7 @@ We want to generate the wrapper for pango. It is related to GTK, so in order to ```sh git submodule add https://github.com/gtk-rs/gir-files +git config -f .gitmodules submodule.gir-files.update none git submodule set-branch --branch master -- ./gir-files ``` -If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. That's the one for pango. Because we already added the gir-files repo, we also have all the other .gir files of the dependencies that we need. Now we can create the unsafe bindings. \ No newline at end of file +We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. Run `git submodule update --checkout` if you want to update the submodule. If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. That's the one for pango. Because we already added the gir-files repo, we also have all the other .gir files of the dependencies that we need. Now we can create the unsafe bindings. \ No newline at end of file diff --git a/book/src/tutorial/preparation.md b/book/src/tutorial/preparation.md index e19fe29b2..840bc96aa 100644 --- a/book/src/tutorial/preparation.md +++ b/book/src/tutorial/preparation.md @@ -23,12 +23,13 @@ cd .. Of course we also need to download and install [gir]. ```sh git submodule add https://github.com/gtk-rs/gir +git config -f .gitmodules submodule.gir.update none git submodule set-branch --branch master -- ./gir cd gir cargo install --path . cd .. ``` -By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. We also set the branch of the submodule to master. +By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. Run `git submodule update --checkout` if you want to update the submodule. Then we set the branch of the submodule to master. If there are any updates to gir in the future, we can install them by opening our project folder `gir-tutorial` and running ```sh From a8e0d0d5ef243ee59087d1d0a40f1b13d0e4f094 Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 8 Jan 2023 15:45:53 +0100 Subject: [PATCH 8/9] Added newline after each sentence to get better diffs --- book/src/config/api.md | 104 ++++++++++++++--------- book/src/config/ffi.md | 19 +++-- book/src/config/introduction.md | 3 +- book/src/config/name_override.md | 3 +- book/src/introduction.md | 31 +++++-- book/src/tutorial/finding_gir_files.md | 36 ++++++-- book/src/tutorial/generate_docs.md | 26 ++++-- book/src/tutorial/handling_errors.md | 45 +++++++--- book/src/tutorial/high_level_rust_api.md | 72 +++++++++++----- book/src/tutorial/introduction.md | 8 +- book/src/tutorial/preparation.md | 12 ++- book/src/tutorial/sys_library.md | 62 +++++++++++--- 12 files changed, 304 insertions(+), 117 deletions(-) diff --git a/book/src/config/api.md b/book/src/config/api.md index 6b9cca020..7cd1152b2 100644 --- a/book/src/config/api.md +++ b/book/src/config/api.md @@ -1,6 +1,7 @@ # API Options -This mode requires you to write another TOML file. [gtk/Gir.toml](https://github.com/gtk-rs/gtk/blob/master/Gir.toml) is a good example. +This mode requires you to write another TOML file. +[gtk/Gir.toml](https://github.com/gtk-rs/gtk/blob/master/Gir.toml) is a good example. ```toml [options] @@ -40,14 +41,17 @@ disable_format = true generate_builder = true ``` -This mode generates only the specified objects. You can either add the object's fullname to the `generate` array or add it to the `manual` array (but in this case, it won't be generated, just used in other functions/methods instead of generating an "ignored" argument). Example: +This mode generates only the specified objects. +You can either add the object's fullname to the `generate` array or add it to the `manual` array (but in this case, it won't be generated, just used in other functions/methods instead of generating an "ignored" argument). +Example: ```toml generate = ["Gtk.Widget", "Gtk.Window"] manual = ["Gtk.Button"] ``` -So in here, both `GtkWidget` and `GtkWindow` will be fully generated and functions/methods using `GtkButton` will be uncommented. To generate code for all global functions, add `Gtk.*` to the `generate` array. +So in here, both `GtkWidget` and `GtkWindow` will be fully generated and functions/methods using `GtkButton` will be uncommented. +To generate code for all global functions, add `Gtk.*` to the `generate` array. To also generate a `Builder` struct for a widget, it needs to be set with the `generate_builder` flag in object configuration: @@ -303,7 +307,8 @@ status = "generate" generate_doc = false ``` -Which will prevent gir from generating `stock_list_ids`. If you want to specify +Which will prevent gir from generating `stock_list_ids`. +If you want to specify that a function will be manually implemented, you can use: ```toml @@ -339,10 +344,13 @@ status = "generate" Constants also support `version` and `cfg_condition` fields. In various cases, GObjects or boxed types can be used from multiple threads -and have certain concurrency guarantees. This can be configured with the -`concurrency` setting at the top-level options or per object. It will +and have certain concurrency guarantees. +This can be configured with the +`concurrency` setting at the top-level options or per object. +It will automatically implement the `Send` and `Sync` traits for the resulting object -and set appropriate trait bounds for signal callbacks. The default is `none`, +and set appropriate trait bounds for signal callbacks. +The default is `none`, and apart from that `send` and `send+sync` are supported. ```toml @@ -358,7 +366,8 @@ concurrency = "send+sync" Note that `send` is only valid for types that are either not reference counted (i.e. `clone()` copies the object) or that are read-only (i.e. no API for -mutating the object exists). `send+sync` is valid if the type can be sent to +mutating the object exists). +`send+sync` is valid if the type can be sent to different threads and all API allows simultaneous calls from different threads due to internal locking via e.g. a mutex. @@ -376,7 +385,8 @@ types are wrong in autogenerated functions that have such objects as argument. This can be overridden with the `ref_mode` configuration. Getters are automatically renamed to comply with Rust codying style guidelines. -However, this can cause name clashes with existing functions. If you want to +However, this can cause name clashes with existing functions. +If you want to bypass the automatic renaming mechanism, use `bypass_auto_rename = true`: ```toml @@ -389,7 +399,8 @@ name = "Gtk.TextBuffer" bypass_auto_rename = true ``` -Some constructors are not annotated as `constructor` in the `gir` files. In +Some constructors are not annotated as `constructor` in the `gir` files. +In order for the naming convention to be applied, you can force a function to be considered as a constructor: @@ -403,10 +414,13 @@ constructor = true ## conversion_type "Option" The `conversion_type` variant `Option` is available for types `T` implementing -`glib::TryFromGlib`. As a reminder, this allows +`glib::TryFromGlib`. +As a reminder, this allows implementing `FromGlib` for `Option` and usually goes alongside with `ToGlib` -for both `T` and `Option`. In this case, `Option` will be used for return -values (including ffi output arguments). For in-arguments, except if the +for both `T` and `Option`. +In this case, `Option` will be used for return +values (including ffi output arguments). +For in-arguments, except if the parameter is declared `mandatory`, `impl Into>` so that either an `Option` or `T` can be used. @@ -424,7 +438,8 @@ The type `ClockTime` implements `glib::TryFromGlib` (and `Option`. Additionally, the user can instruct `gir` to `expect` `Some` or `Ok` results for -specific arguments or return values. E.g.: +specific arguments or return values. +E.g.: ``` rust [[object]] @@ -438,12 +453,9 @@ manual_traits = ["ClockExtManual"] mandatory = true ``` -In the above example, the user instructs gir to consider the `internal` argument -(which also happens to be an out argument) with type gir `Gst.ClockTime` can be -represented as a `ClockTime` without the `Option`. This argument is actually -part of a set of output arguments. With the above gir declaration, the generated -signature is the following (the implementation takes care of `expect`ing the -value to be defined): +In the above example, the user instructs gir to consider the `internal` argument (which also happens to be an out argument) with type gir `Gst.ClockTime` can be represented as a `ClockTime` without the `Option`. +This argument is actually part of a set of output arguments. +With the above gir declaration, the generated signature is the following (the implementation takes care of `expect`ing the value to be defined): ``` rust fn get_calibration( @@ -471,17 +483,19 @@ For a return value, the mandatory declaration reads: The `conversion_type` variant `Result` is available for types `T` implementing `glib::TryFromGlib` where `Err` is neither `GlibNoneError` nor -`GlibNoneOrInvalidError`. In this case, `Result` will be used for +`GlibNoneOrInvalidError`. +In this case, `Result` will be used for return values (including `ffi` output arguments) and the type itself in argument position. In `gstreamer-rs`, the C type `GstStateChangeReturn` can represent both a -successful or an error return value. In Rust, the `Result` `enum` is the -idiomatic way of returning an error. In `gstreamer-rs`, bindings to functions -returning `GstStateChangeReturn` had to be manually implemented so as to return -`Result`. Note that in this case, the type -implementing `TryFromGlib` is `StateChangeSuccess` and not -`GstStateChangeReturn`. These functions can be auto-generated using: +successful or an error return value. +In Rust, the `Result` `enum` is the idiomatic way of returning an error. +In `gstreamer-rs`, bindings to functions +returning `GstStateChangeReturn` had to be manually implemented so as to return `Result`. +Note that in this case, the type implementing `TryFromGlib` is `StateChangeSuccess` and not +`GstStateChangeReturn`. +These functions can be auto-generated using: ``` rust [[object]] @@ -497,18 +511,15 @@ must_use = true ## Boxed types vs. BoxedInline types For boxed types / records, gir auto-detects `copy`/`free` or `ref`/`unref` -function pairs for memory management on records. It falls back to generic -`g_boxed_copy`/`g_boxed_free` if these are not found, based on an existing -implementation of `get_type`. Otherwise no record implementation can be -generated. +function pairs for memory management on records. +It falls back to generic `g_boxed_copy`/`g_boxed_free` if these are not found, based on an existing implementation of `get_type`. +Otherwise no record implementation can be generated. This works for the majority of boxed types, which are literally boxed: their -memory is always allocated on the heap and memory management is left to the C -library. Some boxed types, however, are special and in C code they are usually -allocated on the stack or inline inside another struct. As such, their struct -definition is public and part of the API/ABI. Usually these types are -relatively small and allocated regularly, which would make heap allocations -costly. +memory is always allocated on the heap and memory management is left to the C library. +Some boxed types, however, are special and in C code they are usually allocated on the stack or inline inside another struct. +As such, their struct definition is public and part of the API/ABI. +Usually these types are relatively small and allocated regularly, which would make heap allocations costly. These special boxed types are usually allocated by the caller of the C functions, and the functions are then only filling in the memory and taking @@ -533,7 +544,8 @@ from one value into another one, and to free any resources that might be stored in values of that boxed types. By default the memory is zero-initialized and copying is done via -`std::ptr::copy()`. If the boxed type contains memory that needs to be freed +`std::ptr::copy()`. +If the boxed type contains memory that needs to be freed then these functions must be provided. The following configuration is equivalent with the one above. @@ -550,13 +562,17 @@ clear_function_expression = "|_ptr| ()" ## Generation in API mode -To generate the Rust-user API level, The command is very similar to the previous one. It's better to not put this output in the same directory as where the FFI files are. Just run: +To generate the Rust-user API level, The command is very similar to the previous one. +It's better to not put this output in the same directory as where the FFI files are. +Just run: ```sh cargo run --release -- -c YourGirFile.toml -d ../gir-files -o the-output-directory ``` -Now it should be done. Just go to the output directory (so `the-output-directory/auto` in our case) and try to build using `cargo build`. Don't forget to update your dependencies in both projects: nothing much to do in the FFI/sys one but the Rust-user API level will need to have a dependency over the FFI/sys one. +Now it should be done. +Just go to the output directory (so `the-output-directory/auto` in our case) and try to build using `cargo build`. +Don't forget to update your dependencies in both projects: nothing much to do in the FFI/sys one but the Rust-user API level will need to have a dependency over the FFI/sys one. Now, at your crate entry point (generally `lib.rs`), add the following to include all generated files: @@ -566,7 +582,8 @@ pub use auto::*; ## Add manual bindings alongside generated code -Unfortunately, `gir` isn't perfect (yet) and will certainly not be able to generate all the code on its own. So here's what a `gir` generated folder looks like: +Unfortunately, `gir` isn't perfect (yet) and will certainly not be able to generate all the code on its own. +So here's what a `gir` generated folder looks like: ```text - your_folder @@ -580,7 +597,10 @@ Unfortunately, `gir` isn't perfect (yet) and will certainly not be able to gener |- (all files generated by gir) ``` -You can add your manual bindings directly inside the `src` folder (at the same level as `lib.rs`). Then don't forget to reexport them. Let's say you added a `Color` type in a `color.rs` file. You need to add in `lib.rs`: +You can add your manual bindings directly inside the `src` folder (at the same level as `lib.rs`). +Then don't forget to reexport them. +Let's say you added a `Color` type in a `color.rs` file. +You need to add in `lib.rs`: ```rust // We make the type public for the API users. diff --git a/book/src/config/ffi.md b/book/src/config/ffi.md index 6aaff6017..60a9bc63a 100644 --- a/book/src/config/ffi.md +++ b/book/src/config/ffi.md @@ -1,14 +1,19 @@ # FFI Options -In FFI (`-m sys`) mode, `gir` generates as much as it can. So in this mode, the TOML file is mostly used to ignore some objects. To do so, you need to add its fullname to an `ignore` array. Example: +In FFI (`-m sys`) mode, `gir` generates as much as it can. +So in this mode, the TOML file is mostly used to ignore some objects. +To do so, you need to add its fullname to an `ignore` array. +Example: ```toml ignore = ["Gtk.Widget", "Gtk.Window"] ``` -And that's all! Neither `GtkWidget` nor `GtkWindow` (alongside with their functions) will be generated. +And that's all. +Neither `GtkWidget` nor `GtkWindow` (alongside with their functions) will be generated. -You also need to add any needed external libraries in the "external_libraries" parameter. Example: +You also need to add any needed external libraries in the "external_libraries" parameter. +Example: ```toml [options] @@ -66,12 +71,16 @@ cfg_condition = "feature = \"egl\"" ## Generation in FFI mode -When you're ready, let's generate the FFI part. In the command we'll execute, `../gir-files` is where the directory with your `.gir` files is. (But again, you can just clone the [gir-files repository](https://github.com/gtk-rs/gir-files) and add your file(s) in it). Then let's run the command: +When you're ready, let's generate the FFI part. +In the command we'll execute, `../gir-files` is where the directory with your `.gir` files is. +(But again, you can just clone the [gir-files repository](https://github.com/gtk-rs/gir-files) and add your file(s) in it). +Then let's run the command: ```sh cargo run --release -- -c YourSysGirFile.toml -d ../gir-files -m sys -o the-output-directory-sys ``` -The generated files will be placed in `the-output-directory-sys`. Just take care about the dependencies and the crate's name generated in the `Cargo.toml` file (update them if they don't work as expected). +The generated files will be placed in `the-output-directory-sys`. +Just take care about the dependencies and the crate's name generated in the `Cargo.toml` file (update them if they don't work as expected). You now have the sys part of your binding! diff --git a/book/src/config/introduction.md b/book/src/config/introduction.md index ca7b91753..09add29fa 100644 --- a/book/src/config/introduction.md +++ b/book/src/config/introduction.md @@ -1,6 +1,7 @@ # Configuration -GIR uses two configurations files, one for generating the FFI part of the bindings and the other file for the Rust API. The configuration files must be named `Gir.toml` +GIR uses two configurations files, one for generating the FFI part of the bindings and the other file for the Rust API. +The configuration files must be named `Gir.toml` - The FFI configuration allows things such as ignoring objects, overriding the minimum required version for a specific type or renaming the generated crate name. diff --git a/book/src/config/name_override.md b/book/src/config/name_override.md index ffad9fb0d..70d14b37e 100644 --- a/book/src/config/name_override.md +++ b/book/src/config/name_override.md @@ -1,6 +1,7 @@ # Crate name overrides -`gir` uses simple rule to convert a namespace to a crate name and it sometimes goes wrong. For example, "WebKit2WebExtension" namespace will be converted to "web_kit2_web_extension", which looks bad. +`gir` uses simple rule to convert a namespace to a crate name and it sometimes goes wrong. +For example, "WebKit2WebExtension" namespace will be converted to "web_kit2_web_extension", which looks bad. To fix it, the `crate_name_overrides` option can be used. diff --git a/book/src/introduction.md b/book/src/introduction.md index b470e75a6..515369839 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -1,26 +1,43 @@ # Introduction -[gir] is a tool to automatically generate FFI bindings and a safe wrapper for a library written in C. All you need to be able to use it, is a `.gir` file containing the [GObject introspection](https://gi.readthedocs.io/en/latest/) data for the library you want to create the bindings for, as well as the `.gir` files for all its dependencies. Have a look at the tutorial if you don't know how to [find the .gir files](tutorial/finding_gir_files.md). If your library does not provide a `.gir` file, unfortunately you cannot use [gir], but maybe you can try [rust-bindgen](https://github.com/rust-lang/rust-bindgen). +[gir] is a tool to automatically generate FFI bindings and a safe wrapper for a library written in C. +All you need to be able to use it, is a `.gir` file containing the [GObject introspection](https://gi.readthedocs.io/en/latest/) data for the library you want to create the bindings for, as well as the `.gir` files for all its dependencies. +Have a look at the tutorial if you don't know how to [find the .gir files](tutorial/finding_gir_files.md). +If your library does not provide a `.gir` file, unfortunately you cannot use [gir], but maybe you can try [rust-bindgen](https://github.com/rust-lang/rust-bindgen). -This book contains a tutorial on how to use [gir]. As an example we will create the bindings for [Pango](https://docs.gtk.org/Pango/). In many cases you will be able to follow the same steps with your library. If you are already familiar with [gir] and you just want to look up details about the configuration files, feel free to skip ahead to the documentation of the [configuration files](config/introduction.md). +This book contains a tutorial on how to use [gir]. +As an example we will create the bindings for [Pango](https://docs.gtk.org/Pango/). +In many cases you will be able to follow the same steps with your library. +If you are already familiar with [gir] and you just want to look up details about the configuration files, feel free to skip ahead to the documentation of the [configuration files](config/introduction.md). ## General steps -[gir] tries to make it as simple as possible to generate a safe wrapper for your C library. The process can be divided into four steps that correspond to the four operating modes gir has. +[gir] tries to make it as simple as possible to generate a safe wrapper for your C library. +The process can be divided into four steps that correspond to the four operating modes gir has. - Generating unsafe bindings: -In this step, the low-level FFI bindings are created from the supplied `*.gir` files. These are essentially direct calls into the related C library and are unsafe. The resulting crate is typically appended with -sys. The operating mode is `sys`. +In this step, the low-level FFI bindings are created from the supplied `*.gir` files. +These are essentially direct calls into the related C library and are unsafe. +The resulting crate is typically appended with -sys. +The operating mode is `sys`. - Generating a safe wrapper: -Next, another crate for a layer on top of these unsafe (sys) bindings is created, which makes them safe for use in general Rust. The operating mode is `normal`. +Next, another crate for a layer on top of these unsafe (sys) bindings is created, which makes them safe for use in general Rust. +The operating mode is `normal`. - Checking for missing types/methods: The operating mode `not_bound` allows you to see the detected types/methods that will not be generated for whatever reasons. - Adding documentation: -After the safe wrapper is created, gir can even generate the documentation for us. Use the operating mode `doc` to do so. +After the safe wrapper is created, gir can even generate the documentation for us. +Use the operating mode `doc` to do so. ## Regenerating the bindings and wrapper -In order to generate the bindings and the wrapper for the first time, the above mentioned steps should be followed. When you want to regenerate the crates because e.g. the library was updated, you can simplify the process by running the helper script `./generator.py`. The script detects `Gir.toml` configurations in the current directory and sub-directories (or the paths passed on the command-line) and generates "normal" or "sys" crates for it. Alternatively `--embed-docs` can be passed to prepare source-code for a documentation built by moving all documentation into it. For a complete overview of available options, pass `--help`. +In order to generate the bindings and the wrapper for the first time, the above mentioned steps should be followed. +When you want to regenerate the crates because e.g. +the library was updated, you can simplify the process by running the helper script `./generator.py`. +The script detects `Gir.toml` configurations in the current directory and sub-directories (or the paths passed on the command-line) and generates "normal" or "sys" crates for it. +Alternatively `--embed-docs` can be passed to prepare source-code for a documentation built by moving all documentation into it. +For a complete overview of available options, pass `--help`. ## GIR format reference It can always be useful to look at the [reference](https://gi.readthedocs.io/en/latest/annotations/giannotations.html) or [schema](https://gitlab.gnome.org/GNOME/gobject-introspection/blob/master/docs/gir-1.2.rnc). diff --git a/book/src/tutorial/finding_gir_files.md b/book/src/tutorial/finding_gir_files.md index 5f23e8fea..9c8fa0552 100644 --- a/book/src/tutorial/finding_gir_files.md +++ b/book/src/tutorial/finding_gir_files.md @@ -1,10 +1,26 @@ # Where can I find those .gir files? -There are multiple ways you can get the needed `.gir` files. The `*.gir` you need corresponds to the library you want to generate bindings for. If you have the library installed, you can search for the `.gir` file under `/usr/share/gir-1.0/`. Having the library installed is a good idea so that you can test the generated code. Otherwise you should be able to get it from the package that installs the library. Ubuntu for example allows you to [search](https://packages.ubuntu.com/) for packages and download them via their website. You can copy the `.gir` file of your library to the root of your project folder. You don't have to store all `.gir` files in the same folder. You can add multiple paths by changing the `girs_directories` field in the Gir.toml files. More on this in the next chapters. +There are multiple ways you can get the needed `.gir` files. +The `*.gir` you need corresponds to the library you want to generate bindings for. +If you have the library installed, you can search for the `.gir` file under `/usr/share/gir-1.0/`. +Having the library installed is a good idea so that you can test the generated code. +Otherwise you should be able to get it from the package that installs the library. +Ubuntu for example allows you to [search](https://packages.ubuntu.com/) for packages and download them via their website. +You can copy the `.gir` file of your library to the root of your project folder. +You don't have to store all `.gir` files in the same folder. +You can add multiple paths by changing the `girs_directories` field in the Gir.toml files. +More on this in the next chapters. -Have a look at the .gir file of your library. At the beginning of the file, you probably see something similar to ``. "GObject" in this case would be a dependency and you will have to find the .gir file for your dependencies as well. In most cases it will be enough to follow the next two steps of the tutorial to get all needed files. +Have a look at the .gir file of your library. +At the beginning of the file, you probably see something similar to ``. +"GObject" in this case would be a dependency and you will have to find the .gir file for your dependencies as well. +In most cases it will be enough to follow the next two steps of the tutorial to get all needed files. ## GTK dependencies -If your library depends on GTK libraries, the recommended way to get the `.gir` files for them is to add the [gir-files repo](https://github.com/gtk-rs/gir-files) as a submodule as well. It's the recommended way, because some of the `.gir` files included in the libraries are invalid (missing or invalid annotations for example). These errors are already fixed in the gir files from the repo. Otherwise you could use the above mentioned methods to find the files and run the [script](https://github.com/gtk-rs/gir-files/blob/master/fix.sh) to fix the `.gir` files available in the gir-files repository (and only them!). You can run it like this (at the same level of the `.gir` files you want to patch): +If your library depends on GTK libraries, the recommended way to get the `.gir` files for them is to add the [gir-files repo](https://github.com/gtk-rs/gir-files) as a submodule as well. +It's the recommended way, because some of the `.gir` files included in the libraries are invalid (missing or invalid annotations for example). +These errors are already fixed in the gir files from the repo. +Otherwise you could use the above mentioned methods to find the files and run the [script](https://github.com/gtk-rs/gir-files/blob/master/fix.sh) to fix the `.gir` files available in the gir-files repository (and only them!). +You can run it like this (at the same level of the `.gir` files you want to patch): ```sh sh fix.sh @@ -14,14 +30,22 @@ sh fix.sh For GStreamer related dependencies, follow the above mentioned steps but add this [repo](https://gitlab.freedesktop.org/gstreamer/gir-files-rs) instead. ## Other dependencies -If you have other dependencies, you have to find the files yourself. They can often be found in the repo containing the source of your dependencies or if you have them installed, you might find them under `/usr/share/gir-1.0/` again. +If you have other dependencies, you have to find the files yourself. +They can often be found in the repo containing the source of your dependencies or if you have them installed, you might find them under `/usr/share/gir-1.0/` again. ## Example -We want to generate the wrapper for pango. It is related to GTK, so in order to get its .gir files, we use the recommended way. While being in the project folder `git-tutorial`, we add the [gir-files repo](https://github.com/gtk-rs/gir-files) as a submodule and set the branch of the submodule to master. +We want to generate the wrapper for pango. +It is related to GTK, so in order to get its .gir files, we use the recommended way. +While being in the project folder `git-tutorial`, we add the [gir-files repo](https://github.com/gtk-rs/gir-files) as a submodule and set the branch of the submodule to master. ```sh git submodule add https://github.com/gtk-rs/gir-files git config -f .gitmodules submodule.gir-files.update none git submodule set-branch --branch master -- ./gir-files ``` -We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. Run `git submodule update --checkout` if you want to update the submodule. If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. That's the one for pango. Because we already added the gir-files repo, we also have all the other .gir files of the dependencies that we need. Now we can create the unsafe bindings. \ No newline at end of file +We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. +Run `git submodule update --checkout` if you want to update the submodule. +If you look into `gir-files`, you'll see a file named `Pango-1.0.gir`. +That's the one for pango. +Because we already added the gir-files repo, we also have all the other .gir files of the dependencies that we need. +Now we can create the unsafe bindings. \ No newline at end of file diff --git a/book/src/tutorial/generate_docs.md b/book/src/tutorial/generate_docs.md index 1e7fc06cf..64a4c1e86 100644 --- a/book/src/tutorial/generate_docs.md +++ b/book/src/tutorial/generate_docs.md @@ -1,5 +1,6 @@ # Generating documentation -And finally the last feature! Just run the following command in the folder of the safe wrapper crate: +And finally the last feature. +Just run the following command in the folder of the safe wrapper crate: ```sh gir -c Gir.toml -d ../gir-files --doc-target-path docs.md -m doc @@ -9,7 +10,9 @@ gir -c Gir.toml -d ../gir-files --doc-target-path docs.md -m doc * `--doc-target-path docs.md`: flag to select the name of the markdown file containing the documentation. * `-m doc`: flag to select the work mode for generating the documentation. -It'll generate a markdown file if everything went fine. It contains all of the crate's documentation. If you want to put it into your crate's source code like "normal" doc comments, run: +It'll generate a markdown file if everything went fine. +It contains all of the crate's documentation. +If you want to put it into your crate's source code like "normal" doc comments, run: ```sh cargo install rustdoc-stripper @@ -18,7 +21,9 @@ rustdoc-stripper -g -o docs.md And now your crate should be completely documented as expected! -Running the above commands again would duplicate the doc comments. Make sure to first remove the old ones before running the command again. You can do this by running the following commands: +Running the above commands again would duplicate the doc comments. +Make sure to first remove the old ones before running the command again. +You can do this by running the following commands: ```sh rustdoc-stripper -s -n @@ -31,8 +36,17 @@ Try building the documentation and also try it with the various features you mig cargo doc ``` -Congratulations, we are done! You have successfully created the safe wrapper for a C library! +Congratulations, we are done. +You have successfully created the safe wrapper for a C library! -You can easily publish your generated bindings and the wrapper to crates.io to allow others to use it. Publishing crates is easy but keep in mind that they need to be maintained as well. We set up the project folder in a way that easily allows sharing the code. All that is needed is to add some information to your Cargo.toml. Gir will not override them when you re-generate bindings. Easy, right? If this is your first time publishing a crate, you can find a detailed guide [here](https://doc.rust-lang.org/cargo/reference/publishing.html). +You can easily publish your generated bindings and the wrapper to crates.io to allow others to use it. +Publishing crates is easy but keep in mind that they need to be maintained as well. +We set up the project folder in a way that easily allows sharing the code. +All that is needed is to add some information to your Cargo.toml. +Gir will not override them when you re-generate bindings. +Easy, right. +If this is your first time publishing a crate, you can find a detailed guide [here](https://doc.rust-lang.org/cargo/reference/publishing.html). -Before you publish the crate, please ensure docs.rs will activate the dox feature and the dox feature of the safe wrapper crate also activates the feature of its dependencies and the unsafe FFI bindings you created. Feel free to go back to the chapter about the [Cargo.toml file of the safe wrapper](high_level_rust_api.md#the-cargotoml-file) to read more about it. If you skip this step, your crate and all crates depending on it will not have documentation available on docs.rs. \ No newline at end of file +Before you publish the crate, please ensure docs.rs will activate the dox feature and the dox feature of the safe wrapper crate also activates the feature of its dependencies and the unsafe FFI bindings you created. +Feel free to go back to the chapter about the [Cargo.toml file of the safe wrapper](high_level_rust_api.md#the-cargotoml-file) to read more about it. +If you skip this step, your crate and all crates depending on it will not have documentation available on docs.rs. \ No newline at end of file diff --git a/book/src/tutorial/handling_errors.md b/book/src/tutorial/handling_errors.md index 8ce7f6b86..4c35a7acd 100644 --- a/book/src/tutorial/handling_errors.md +++ b/book/src/tutorial/handling_errors.md @@ -1,6 +1,7 @@ # Handling generation errors -Luckily there are only a few errors which can happen with [gir] generation. Let's take a look at them. +Luckily there are only a few errors which can happen with [gir] generation. +Let's take a look at them. ### Cannot find macros @@ -24,7 +25,9 @@ error: cannot find macro `assert_initialized_main_thread` in this scope = help: have you added the `#[macro_use]` on the module/import? ``` -In this case you’ll have to implement them yourself. Macros are order-dependent and you *must* insert this code before declaring modules that use it (e.g. `mod auto`). For example, you can add the following to your `lib.rs` file: +In this case you’ll have to implement them yourself. +Macros are order-dependent and you *must* insert this code before declaring modules that use it (e.g. `mod auto`). +For example, you can add the following to your `lib.rs` file: ```rust /// No-op. @@ -47,7 +50,9 @@ macro_rules! assert_initialized_main_thread { ``` -One complication here is that the `assert_initialized_main_thread!` macro depends on the exact library. If it's GTK-based then the above macro is likely correct, unless the library has its own initialization function. If it has its own initialization function it would need to be handled in addition to GTK's here in the same way. +One complication here is that the `assert_initialized_main_thread!` macro depends on the exact library. +If it's GTK-based then the above macro is likely correct, unless the library has its own initialization function. +If it has its own initialization function it would need to be handled in addition to GTK's here in the same way. For non-GTK-based libraries the following macro would handle the initialization function of that library in the same way, or if there is none it would simply do nothing: @@ -60,14 +65,22 @@ macro_rules! assert_initialized_main_thread { ## Missing memory management functions -If [gir] generation fails (for whatever reason), it means you'll have to implement the type yourself. Just like types from other `gtk-rs` crates, you'll need to put it into the "manual" list. Then you need to put the type into the `src` folder (or inside a subfolder, you know how Rust works). +If [gir] generation fails (for whatever reason), it means you'll have to implement the type yourself. +Just like types from other `gtk-rs` crates, you'll need to put it into the "manual" list. +Then you need to put the type into the `src` folder (or inside a subfolder, you know how Rust works). -/!\ Don't forget to reexport the type inside your `src/lib.rs` file! For example, let's take a look at the [requisition.rs](https://github.com/gtk-rs/gtk3-rs/blob/master/gtk/src/requisition.rs) file from the `gtk3` crate. +/!\ Don't forget to reexport the type inside your `src/lib.rs` file. +For example, let's take a look at the [requisition.rs](https://github.com/gtk-rs/gtk3-rs/blob/master/gtk/src/requisition.rs) file from the `gtk3` crate. -Since it's a "simple" type (no pointer, therefore no memory management to do), [gir] doesn't know how to generate it. You'll need to implement some traits by hand like `ToGlibPtr` or `ToGlibPtrMut` (depending on your needs). +Since it's a "simple" type (no pointer, therefore no memory management to do), [gir] doesn't know how to generate it. +You'll need to implement some traits by hand like `ToGlibPtr` or `ToGlibPtrMut` (depending on your needs). ## Bad function generation -In some cases, the generated code isn't correct (array parameters are often an issue). In such cases, it's better to just make the implementation yourself. As an example, let's say you want to implement `Region::is_empty` yourself. A few changes have to be made. Let's start with `Gir.toml`: +In some cases, the generated code isn't correct (array parameters are often an issue). +In such cases, it's better to just make the implementation yourself. +As an example, let's say you want to implement `Region::is_empty` yourself. +A few changes have to be made. +Let's start with `Gir.toml`: ```toml generate = [ @@ -82,9 +95,12 @@ status = "generate" ignore = true ``` -So to sum up what was written above: we removed "GtkSource.Region" from the "generate" list and we created a new entry for it. Then we say to [gir] that it should generate (through `status = "generate"`). However, we also tell it that we don't want the "is_empty" function to be generated. +So to sum up what was written above: we removed "GtkSource.Region" from the "generate" list and we created a new entry for it. +Then we say to [gir] that it should generate (through `status = "generate"`). +However, we also tell it that we don't want the "is_empty" function to be generated. -Now that we've done that, we need to implement it. Let's create a `src/region.rs` file: +Now that we've done that, we need to implement it. +Let's create a `src/region.rs` file: ```rust use glib::object::IsA; @@ -103,9 +119,13 @@ impl> RegionExtManual for O { } ``` -You might wonder: "why not just implementing it on the `Region` type directly?". Because like this, a subclass will also be able to use this trait easily as long as it implements `IsA`. For instance, in gtk, everything that implements `IsA` (so almost every GTK types) can use those methods. +You might wonder: "why not just implementing it on the `Region` type directly?". +Because like this, a subclass will also be able to use this trait easily as long as it implements `IsA`. +For instance, in gtk, everything that implements `IsA` (so almost every GTK types) can use those methods. -As usual, don't forget to reexport the trait. A little tip about reexporting manual traits: in `gtk3-rs`, we create a `src/prelude.rs` file which reexports all traits (both manual and generated ones), making it simpler for users to use them through `use [DEPENDENCY]::prelude::*`. The `src/prelude.rs` file looks like this: +As usual, don't forget to reexport the trait. +A little tip about reexporting manual traits: in `gtk3-rs`, we create a `src/prelude.rs` file which reexports all traits (both manual and generated ones), making it simpler for users to use them through `use [DEPENDENCY]::prelude::*`. +The `src/prelude.rs` file looks like this: ```rust pub use auto::traits::*; @@ -120,7 +140,8 @@ pub use prelude::*; ``` ## Manually defined traits missing from the documentation -If you defined traits manually, you can add them to the "Implements" section in the documentation for classes and interfaces by using the `manual_traits = []` option in the `Gir.toml` file. Here is an example: +If you defined traits manually, you can add them to the "Implements" section in the documentation for classes and interfaces by using the `manual_traits = []` option in the `Gir.toml` file. +Here is an example: ```toml [[object]] diff --git a/book/src/tutorial/high_level_rust_api.md b/book/src/tutorial/high_level_rust_api.md index 8e6c2732b..387f1b275 100644 --- a/book/src/tutorial/high_level_rust_api.md +++ b/book/src/tutorial/high_level_rust_api.md @@ -1,17 +1,26 @@ # Generating the Rust API -In the previous step we successfully created the unsafe bindings of the -sys crate. We are now in the directory of the safe wrapper crate (`gir-tutorial/pango`). +In the previous step we successfully created the unsafe bindings of the -sys crate. +We are now in the directory of the safe wrapper crate (`gir-tutorial/pango`). ## The Cargo.toml file -The Cargo.toml file will not be replaced when you run gir. So it is our responsibility to make sure the information in it is correct. Open the Cargo.toml file and have a look at it. Make sure everything under `[package]` is to your liking. +The Cargo.toml file will not be replaced when you run gir. +So it is our responsibility to make sure the information in it is correct. +Open the Cargo.toml file and have a look at it. +Make sure everything under `[package]` is to your liking. Add the following lines to the file: ```toml [package.metadata.docs.rs] features = ["dox"] ``` -This automatically activates the `dox` feature if you chose to publish the bindings and docs.rs tries to build the documentation. The `dox` feature skips linking the C libraries and allows docs.rs to build the documentation without having the underlying libraries installed. Even if you don't plan to publish it, this line is not going to hurt. - -We also need to add `libc`, `bitflags`, `glib` and `glib-sys` and all other dependencies we used in the sys crate as dependencies. Because we are creating a wrapper for the sys crate, which we generated in the previous chapter, we also need to add the sys crate to the list of dependencies. In the automatically generated code, the sys crate is always called `ffi`, so we need to rename the sys crate in our `Cargo.toml`. For our example, this results in the following dependencies: +This automatically activates the `dox` feature if you chose to publish the bindings and docs.rs tries to build the documentation. +The `dox` feature skips linking the C libraries and allows docs.rs to build the documentation without having the underlying libraries installed. +Even if you don't plan to publish it, this line is not going to hurt. + +We also need to add `libc`, `bitflags`, `glib` and `glib-sys` and all other dependencies we used in the sys crate as dependencies. +Because we are creating a wrapper for the sys crate, which we generated in the previous chapter, we also need to add the sys crate to the list of dependencies. +In the automatically generated code, the sys crate is always called `ffi`, so we need to rename the sys crate in our `Cargo.toml`. +For our example, this results in the following dependencies: ```toml [dependencies] libc = "0.2" @@ -30,7 +39,10 @@ package = "gobject-sys" git = "https://github.com/gtk-rs/gtk-rs-core" ``` -In order to make the features of the sys crate available for users of your safe wrapper, you need to add features. Copy the `[features]` part of the Cargo.toml of your sys crate and paste it into the Cargo.toml of the normal crate. The features are supposed to activate the corresponding features of the sys crate, so you need to make some changes. If for example you have the following sys features: +In order to make the features of the sys crate available for users of your safe wrapper, you need to add features. +Copy the `[features]` part of the Cargo.toml of your sys crate and paste it into the Cargo.toml of the normal crate. +The features are supposed to activate the corresponding features of the sys crate, so you need to make some changes. +If for example you have the following sys features: ```toml [features] @@ -97,7 +109,10 @@ dox = ["glib/dox", "gobject/dox", "ffi/dox"] ``` ## The lib.rs file -The lib.rs file will not be replaced when you run gir. All the code that gir will generate for us is going to be in src/auto. We need to include all `auto` files in our library. To do so, let's update the `src/lib.rs` file as follows: +The lib.rs file will not be replaced when you run gir. +All the code that gir will generate for us is going to be in src/auto. +We need to include all `auto` files in our library. +To do so, let's update the `src/lib.rs` file as follows: ```rust #![cfg_attr(feature = "dox", feature(doc_cfg))] @@ -108,7 +123,8 @@ mod auto; ## The Gir.toml file -As you certainly guessed, we have to fill our `Gir.toml` file for the normal crate as well. Let's write it: +As you certainly guessed, we have to fill our `Gir.toml` file for the normal crate as well. +Let's write it: ```toml [options] @@ -127,13 +143,17 @@ generate = [] manual = [] ``` -Many of these options look familiar from the last chapter but there are also a few new things in here. Let's take a look at them: +Many of these options look familiar from the last chapter but there are also a few new things in here. +Let's take a look at them: * `work_mode` value is now set to `normal`, it means it'll generate the high-level Rust api instead of the sys-level. * `generate_safety_asserts` is used to generates checks to ensure that, or any other kind of initialization needed before being able to use the library. * `deprecate_by_min_version` is used to generate a [Rust "#[deprecated]"](https://doc.rust-lang.org/edition-guide/rust-2018/the-compiler/an-attribute-for-deprecation.html) attribute based on the deprecation information provided by the `.gir` file. -* `generate = []`: this line currently does nothing. We say to [gir] to generate nothing. We'll fill it later on. -* `manual = []`: this line currently does nothing. We can let [gir] know about objects which it does not have to generate code for. +* `generate = []`: this line currently does nothing. +We say to [gir] to generate nothing. +We'll fill it later on. +* `manual = []`: this line currently does nothing. +We can let [gir] know about objects which it does not have to generate code for. Let's make a first generation of our high-level Rust API! @@ -141,9 +161,13 @@ Let's make a first generation of our high-level Rust API! gir -o . ``` -If you take a look at which files and folders were created, you'll see a new "auto" folder inside "src". This folder contains all the generated code. It doesn't contain anything though. Which makes sense since we're generating nothing. +If you take a look at which files and folders were created, you'll see a new "auto" folder inside "src". +This folder contains all the generated code. +It doesn't contain anything though. +Which makes sense since we're generating nothing. -Now it's time to introduce you to a whole new [gir] mode: `not_bound`. Let's give it a try: +Now it's time to introduce you to a whole new [gir] mode: `not_bound`. +Let's give it a try: ```console > gir -o . -m not_bound @@ -278,14 +302,18 @@ Now it's time to introduce you to a whole new [gir] mode: `not_bound`. Let's giv [NOT GENERATED FUNCTION] Pango.unichar_direction because of Pango.Direction ``` -We now have the list of all the not-yet generated items. Quite convenient! There can be different kinds of not generated items: +We now have the list of all the not-yet generated items. +Quite convenient. +There can be different kinds of not generated items: * `[NOT GENERATED]`: Objects marked with `[NOT GENERATED]` are objects that we can generate, but we did not (yet) add to the `generate` array. * `[NOT GENERATED PARENT]`: -These objects live in a dependency of the current library. These are the objects we will add to the `manual` array in the following steps. +These objects live in a dependency of the current library. +These are the objects we will add to the `manual` array in the following steps. * `[NOT GENERATED FUNCTION]`: -These are global functions that were not generated. To fix it, we just add `"NameOfYourLibrary.*"` to the `generate` array in the Git.toml and add the following line to your src/lib.rs file: +These are global functions that were not generated. +To fix it, we just add `"NameOfYourLibrary.*"` to the `generate` array in the Git.toml and add the following line to your src/lib.rs file: ```rust pub use auto::functions::*; ``` @@ -298,12 +326,15 @@ In order to generate the code for the safe wrapper, we follow these steps until - Add it to the `generate` array in the Gir.toml file - Run `gir -o .` to generate the code - Open the generated files under src/auto and have a look at them -- Search for `/*Ignored*/`. If the type name following `/*Ignored*/` is prepended by `[crate_name]::` (e.g `Ignored*/&glib::MarkupParseContext`), - - then we add it to the `manual` array. By doing so we tell [gir] that those types have been generated somewhere else and that they can be used just like the other types. +- Search for `/*Ignored*/`. +If the type name following `/*Ignored*/` is prepended by `[crate_name]::` (e.g `Ignored*/&glib::MarkupParseContext`), + - then we add it to the `manual` array. + By doing so we tell [gir] that those types have been generated somewhere else and that they can be used just like the other types. - Otherwise, the type comes from the current crate and we just put it into the `generate` list of the `Gir.toml` file. - Start with the first step again -The names of the objects are not the same as the crates names. You have to use the names of the corresponding gir files. +The names of the objects are not the same as the crates names. +You have to use the names of the corresponding gir files. Okay, lets go through that process for a few objects of our example. @@ -311,7 +342,8 @@ Okay, lets go through that process for a few objects of our example. Again, if you do it on another library and it fails and you can't figure out why, don't hesitate to [contact us](https://gtk-rs.org/contact)! -At this point, you should have almost everything you need. Let's have a look at errors that can happen in this process. +At this point, you should have almost everything you need. +Let's have a look at errors that can happen in this process. [gir]: https://github.com/gtk-rs/gir diff --git a/book/src/tutorial/introduction.md b/book/src/tutorial/introduction.md index 302d0913c..6c4b92ed6 100644 --- a/book/src/tutorial/introduction.md +++ b/book/src/tutorial/introduction.md @@ -1,5 +1,11 @@ # Tutorial -In this tutorial, we go through the basic steps needed to generate a safe wrapper for a simple C library. We start with finding the .gir files needed and end with generating the documentation. We also look at a few common errors and how to fix them. Because this can be a bit abstract sometimes, we use the creation of the bindings for [Pango](https://docs.gtk.org/Pango/) as an example. The example continues through all of the chapters of the tutorial. If you follow along until the end, you will have generated a safe wrapper including the documentation. In case you are stuck at any point or there are other errors and you can't figure out what's going on, don't hesitate to reach us so we can give you a hand! +In this tutorial, we go through the basic steps needed to generate a safe wrapper for a simple C library. +We start with finding the .gir files needed and end with generating the documentation. +We also look at a few common errors and how to fix them. +Because this can be a bit abstract sometimes, we use the creation of the bindings for [Pango](https://docs.gtk.org/Pango/) as an example. +The example continues through all of the chapters of the tutorial. +If you follow along until the end, you will have generated a safe wrapper including the documentation. +In case you are stuck at any point or there are other errors and you can't figure out what's going on, don't hesitate to reach us so we can give you a hand! Let's dive right into it and let's set up our project folder! diff --git a/book/src/tutorial/preparation.md b/book/src/tutorial/preparation.md index 840bc96aa..82ffe9006 100644 --- a/book/src/tutorial/preparation.md +++ b/book/src/tutorial/preparation.md @@ -2,7 +2,12 @@ In order to install gir and nicely structure the project, there are a few things to set up. ## Set up the project folder -In order to keep the project folder nicely organized, lets create a folder where we will work in and initialize the repo. We will create two library crates. pango will contain the safe wrapper crate and because it is a wrapper for the unsafe bindings, we create the pango-sys crate within the pango crate. If no Cargo.toml file is present in the sys create, a new one will be generated, so lets be safe and delete the automatically created file before we begin. The following commands will set up the project folder as described. +In order to keep the project folder nicely organized, lets create a folder where we will work in and initialize the repo. +We will create two library crates. +pango will contain the safe wrapper crate and because it is a wrapper for the unsafe bindings, we create the pango-sys crate within the pango crate. +If no Cargo.toml file is present in the sys create, a new one will be generated, so lets be safe and delete the automatically created file before we begin. +The following commands will set up the project folder as described. + ```sh mkdir gir-tutorial cd gir-tutorial/ @@ -29,7 +34,10 @@ cd gir cargo install --path . cd .. ``` -By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. Run `git submodule update --checkout` if you want to update the submodule. Then we set the branch of the submodule to master. +By adding it as a submodule, we are able to fetch future updates of the tool and we always exactly know which gir version we used to generate our bindings. +We also change the setting so that the submodule is not automatically checked out, otherwise anyone using your library from git will have the useless submodule checked out. +Run `git submodule update --checkout` if you want to update the submodule. +Then we set the branch of the submodule to master. If there are any updates to gir in the future, we can install them by opening our project folder `gir-tutorial` and running ```sh diff --git a/book/src/tutorial/sys_library.md b/book/src/tutorial/sys_library.md index e7865f1a8..1afe1fd30 100644 --- a/book/src/tutorial/sys_library.md +++ b/book/src/tutorial/sys_library.md @@ -1,12 +1,17 @@ # Generating the FFI library -We have installed [gir], set up our repo and have all the `.gir` files we need. Now let's work on the unsafe bindings of the -sys crate. Let's change into the directory of the sys crate. +We have installed [gir], set up our repo and have all the `.gir` files we need. +Now let's work on the unsafe bindings of the -sys crate. +Let's change into the directory of the sys crate. ```sh cd pango/pango-sys ``` ## The Gir.toml file -The first step is to let [gir] know what to generate. This is what the `Gir.toml` file that we created inside the `pango-sys` folder is for. This file will not be replaced when we run gir again. The file is currently empty so let's add the following to it: +The first step is to let [gir] know what to generate. +This is what the `Gir.toml` file that we created inside the `pango-sys` folder is for. +This file will not be replaced when we run gir again. +The file is currently empty so let's add the following to it: ```toml [options] @@ -24,10 +29,18 @@ single_version_file = true * `min_cfg_version` will be the minimum version supported by the generated bindings. * `target_path` stands for the location where the files will be generated. * `girs_directories` stands for the location of the `.gir` files. -* `work_mode` stands for the mode gir is using. The options here are `sys` and `normal`. -* `single_version_file` is a very useful option when you have a lot of generated files (like we'll have). Instead of generating the gir hash commit used for the generation in the header of all generated files, it'll just write it inside one file, removing `git diff` noise **a lot**. - -You can find out the values for `library` and `version` by looking at the name of the .gir file of your library. In our case it is called Pango-1.0.gir. This tells us that the `library` is Pango and the `version` is 1.0. If you don't know what value to use for `min_cfg_version`, use the same as you use for `version`. If not all `.gir` files that are needed reside in `../../gir-files/`, you can add the path to the other files by changing `girs_directories`. If for example you also have `.gir` files in the root of your project folder, change it to `girs_directories = ["../../gir-files/", "../.."]`. Because we are generating the unsafe bindings, we use the `sys` work mode. +* `work_mode` stands for the mode gir is using. + The options here are `sys` and `normal`. +* `single_version_file` is a very useful option when you have a lot of generated files (like we'll have). + Instead of generating the gir hash commit used for the generation in the header of all generated files, it'll just write it inside one file, removing `git diff` noise **a lot**. + +You can find out the values for `library` and `version` by looking at the name of the .gir file of your library. +In our case it is called Pango-1.0.gir. +This tells us that the `library` is Pango and the `version` is 1.0. +If you don't know what value to use for `min_cfg_version`, use the same as you use for `version`. +If not all `.gir` files that are needed reside in `../../gir-files/`, you can add the path to the other files by changing `girs_directories`. +If for example you also have `.gir` files in the root of your project folder, change it to `girs_directories = ["../../gir-files/", "../.."]`. +Because we are generating the unsafe bindings, we use the `sys` work mode. Let's generate the `sys` crate now: @@ -49,12 +62,20 @@ Now let's try to build it: cargo build ``` -Surprise! It doesn't build at all and you should see a lot of errors. Well, that was expected. We need to add some dependencies in order to make it work. Have a look at the errors of the compiler to find out which are missing. In our example, the compiler throws the following errors: +Surprise. +It doesn't build at all and you should see a lot of errors. +Well, that was expected. +We need to add some dependencies in order to make it work. +Have a look at the errors of the compiler to find out which are missing. +In our example, the compiler throws the following errors: ```rust use of undeclared crate or module `glib` use of undeclared crate or module `gobject` ``` -The dependencies need to be added to the `external_libraries` part. The names of the dependencies are the same as in the .gir files and not what the packages to install the libraries might be called. The compiler told us that the `GLib` and the `GObject` dependencies are missing. Let's update our `Gir.toml` file to fix it: +The dependencies need to be added to the `external_libraries` part. +The names of the dependencies are the same as in the .gir files and not what the packages to install the libraries might be called. +The compiler told us that the `GLib` and the `GObject` dependencies are missing. +Let's update our `Gir.toml` file to fix it: ```toml [options] @@ -71,22 +92,35 @@ external_libraries = [ ] ``` - If one of your .gir files changed or you want to use an updated version of gir to generate the code, there is no need to delete the Cargo.toml or the Cargo.lock files before you regenerate the code. Because we made some changes to the Gir.toml file, we have to run gir again. Changing the content of the external_libraries array means that additional dependencies have to be added. gir does this automatically for you, but only if there is no Cargo.toml and no Cargo.lock file present. Just remove the `Cargo.*` files and run gir again and the additional dependencies will be added. If you made any manual changes to the file, you would have to do these changes again. After regenerating the code, we build the crate to see if the errors are gone. + If one of your .gir files changed or you want to use an updated version of gir to generate the code, there is no need to delete the Cargo.toml or the Cargo.lock files before you regenerate the code. +Because we made some changes to the Gir.toml file, we have to run gir again. +Changing the content of the external_libraries array means that additional dependencies have to be added. +gir does this automatically for you, but only if there is no Cargo.toml and no Cargo.lock file present. +Just remove the `Cargo.*` files and run gir again and the additional dependencies will be added. +If you made any manual changes to the file, you would have to do these changes again. +After regenerating the code, we build the crate to see if the errors are gone. ```sh rm Cargo.* gir -o . cargo build ``` -When executing the above commands, there should not be any errors and everything should work fine. Just to be sure everything was correctly generated, we can run some tests (graciously generated by [gir] as well): +When executing the above commands, there should not be any errors and everything should work fine. +Just to be sure everything was correctly generated, we can run some tests (graciously generated by [gir] as well): ```sh cargo test ``` -Normally, all tests passed. If you get an error when running those tests, it's very likely that the `sys` generation is invalid and/or incomplete. +Normally, all tests passed. +If you get an error when running those tests, it's very likely that the `sys` generation is invalid and/or incomplete. ## The Gir.toml file -This file was automatically generated but it will not be replaced when we run gir again. Make sure to look at your Cargo.toml to optionally add more information to it. If there are any `[features]`, you should try building and testing with these features activated as well. If you'd like, you can also set the default features. This can be useful for example if you want to always activate the newest version unless the user of the crate specifies an older version. - -For our example, we now have a working `sys` crate containing all functions and objects definition. We are done here and can go back to the folder of the wrapper crate: +This file was automatically generated but it will not be replaced when we run gir again. +Make sure to look at your Cargo.toml to optionally add more information to it. +If there are any `[features]`, you should try building and testing with these features activated as well. +If you'd like, you can also set the default features. +This can be useful for example if you want to always activate the newest version unless the user of the crate specifies an older version. + +For our example, we now have a working `sys` crate containing all functions and objects definition. +We are done here and can go back to the folder of the wrapper crate: ```sh cd .. ``` From 824339225dce60c5fdd4f6bf533442332a53a83d Mon Sep 17 00:00:00 2001 From: pentamassiv Date: Sun, 8 Jan 2023 16:08:25 +0100 Subject: [PATCH 9/9] Applied all spelling corrections sugested by Hofer-Julian --- book/src/introduction.md | 8 ++++---- book/src/tutorial/finding_gir_files.md | 4 ++-- book/src/tutorial/high_level_rust_api.md | 2 +- book/src/tutorial/preparation.md | 4 ++-- book/src/tutorial/sys_library.md | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/book/src/introduction.md b/book/src/introduction.md index 515369839..47b1ff0c2 100644 --- a/book/src/introduction.md +++ b/book/src/introduction.md @@ -1,6 +1,6 @@ # Introduction -[gir] is a tool to automatically generate FFI bindings and a safe wrapper for a library written in C. -All you need to be able to use it, is a `.gir` file containing the [GObject introspection](https://gi.readthedocs.io/en/latest/) data for the library you want to create the bindings for, as well as the `.gir` files for all its dependencies. +[gir] is a tool to automatically generate safe wrappers for a C library with [GObject introspection](https://gi.readthedocs.io/en/latest/) information. +In order to use it you need the `.gir` file containing the introspection data for the library you want to create the bindings for, as well as the `.gir` files for all its dependencies. Have a look at the tutorial if you don't know how to [find the .gir files](tutorial/finding_gir_files.md). If your library does not provide a `.gir` file, unfortunately you cannot use [gir], but maybe you can try [rust-bindgen](https://github.com/rust-lang/rust-bindgen). @@ -32,10 +32,10 @@ Use the operating mode `doc` to do so. ## Regenerating the bindings and wrapper -In order to generate the bindings and the wrapper for the first time, the above mentioned steps should be followed. +In order to generate the bindings and the wrapper for the first time, the above-mentioned steps should be followed. When you want to regenerate the crates because e.g. the library was updated, you can simplify the process by running the helper script `./generator.py`. -The script detects `Gir.toml` configurations in the current directory and sub-directories (or the paths passed on the command-line) and generates "normal" or "sys" crates for it. +The script detects `Gir.toml` configurations in the current directory and subdirectories (or the paths passed on the command-line) and generates "normal" or "sys" crates for it. Alternatively `--embed-docs` can be passed to prepare source-code for a documentation built by moving all documentation into it. For a complete overview of available options, pass `--help`. diff --git a/book/src/tutorial/finding_gir_files.md b/book/src/tutorial/finding_gir_files.md index 9c8fa0552..fb923a4d7 100644 --- a/book/src/tutorial/finding_gir_files.md +++ b/book/src/tutorial/finding_gir_files.md @@ -19,7 +19,7 @@ In most cases it will be enough to follow the next two steps of the tutorial to If your library depends on GTK libraries, the recommended way to get the `.gir` files for them is to add the [gir-files repo](https://github.com/gtk-rs/gir-files) as a submodule as well. It's the recommended way, because some of the `.gir` files included in the libraries are invalid (missing or invalid annotations for example). These errors are already fixed in the gir files from the repo. -Otherwise you could use the above mentioned methods to find the files and run the [script](https://github.com/gtk-rs/gir-files/blob/master/fix.sh) to fix the `.gir` files available in the gir-files repository (and only them!). +Otherwise you could use the above-mentioned methods to find the files and run the [script](https://github.com/gtk-rs/gir-files/blob/master/fix.sh) to fix the `.gir` files available in the gir-files repository (and only them!). You can run it like this (at the same level of the `.gir` files you want to patch): ```sh @@ -27,7 +27,7 @@ sh fix.sh ``` ## GStreamer dependencies -For GStreamer related dependencies, follow the above mentioned steps but add this [repo](https://gitlab.freedesktop.org/gstreamer/gir-files-rs) instead. +For GStreamer related dependencies, follow the above-mentioned steps but add this [repo](https://gitlab.freedesktop.org/gstreamer/gir-files-rs) instead. ## Other dependencies If you have other dependencies, you have to find the files yourself. diff --git a/book/src/tutorial/high_level_rust_api.md b/book/src/tutorial/high_level_rust_api.md index 387f1b275..de37a4a21 100644 --- a/book/src/tutorial/high_level_rust_api.md +++ b/book/src/tutorial/high_level_rust_api.md @@ -336,7 +336,7 @@ If the type name following `/*Ignored*/` is prepended by `[crate_name]::` (e.g ` The names of the objects are not the same as the crates names. You have to use the names of the corresponding gir files. -Okay, lets go through that process for a few objects of our example. +Okay, let's go through that process for a few objects of our example. 🚧 TODO: Add remaining steps of the pango example 🚧 diff --git a/book/src/tutorial/preparation.md b/book/src/tutorial/preparation.md index 82ffe9006..62a31691a 100644 --- a/book/src/tutorial/preparation.md +++ b/book/src/tutorial/preparation.md @@ -2,10 +2,10 @@ In order to install gir and nicely structure the project, there are a few things to set up. ## Set up the project folder -In order to keep the project folder nicely organized, lets create a folder where we will work in and initialize the repo. +In order to keep the project folder nicely organized, let's create a folder where we will work in and initialize the repo. We will create two library crates. pango will contain the safe wrapper crate and because it is a wrapper for the unsafe bindings, we create the pango-sys crate within the pango crate. -If no Cargo.toml file is present in the sys create, a new one will be generated, so lets be safe and delete the automatically created file before we begin. +If no Cargo.toml file is present in the sys create, a new one will be generated, so let's be safe and delete the automatically created file before we begin. The following commands will set up the project folder as described. ```sh diff --git a/book/src/tutorial/sys_library.md b/book/src/tutorial/sys_library.md index 1afe1fd30..8206492b1 100644 --- a/book/src/tutorial/sys_library.md +++ b/book/src/tutorial/sys_library.md @@ -38,7 +38,7 @@ You can find out the values for `library` and `version` by looking at the name o In our case it is called Pango-1.0.gir. This tells us that the `library` is Pango and the `version` is 1.0. If you don't know what value to use for `min_cfg_version`, use the same as you use for `version`. -If not all `.gir` files that are needed reside in `../../gir-files/`, you can add the path to the other files by changing `girs_directories`. +If not all needed `.gir` files reside in `../../gir-files/`, you can add the path to the other files by changing `girs_directories`. If for example you also have `.gir` files in the root of your project folder, change it to `girs_directories = ["../../gir-files/", "../.."]`. Because we are generating the unsafe bindings, we use the `sys` work mode.