Skip to content

Commit 838e89f

Browse files
committed
- add emscripten support and basic example
- skip layout tests in pre-generated bindings to avoid errors on emscripten - Test more build configuratioions in github CI - support using pkg-config to find glfw libs - properly document features/linker flags.
1 parent 6c740ac commit 838e89f

File tree

10 files changed

+351
-146
lines changed

10 files changed

+351
-146
lines changed

.github/workflows/build.yml

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ jobs:
2424
sudo apt update
2525
sudo apt install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxext-dev libwayland-dev libxkbcommon-dev
2626
27+
# To check vulkan bindings.
28+
- name: Prepare Vulkan SDK
29+
uses: humbletim/[email protected]
30+
with:
31+
vulkan-query-version: latest
32+
vulkan-components: Vulkan-Headers
33+
vulkan-use-cache: true
34+
2735
- name: Source build with static link
2836
shell: bash
2937
run: cargo clean && cargo run -vv --example=version --features=src_build,static_link
@@ -36,32 +44,41 @@ jobs:
3644
shell: bash
3745
# linux pre-built static libs are not provided, so, static linkign requires src_build.
3846
if: matrix.os != 'ubuntu'
39-
run: cargo clean && cargo run -vv --example=version --features=static_link
47+
run: cargo clean && cargo run -vv --example=version --features=static_link,prebuilt_libs
4048

49+
- name: Prebuilt libs with shared link
50+
shell: bash
51+
if: matrix.os != 'ubuntu'
52+
run: cargo clean && cargo run -vv --example=version --features=prebuilt_libs
53+
4154
# We delay this, because we don't want the previous steps to "accidentally" succeed
4255
# by linking to system libraries (on linux).
4356
- name: Install Glfw Packages (Linux)
4457
if: matrix.os == 'ubuntu'
4558
shell: bash
4659
run: sudo apt install libglfw3-dev
60+
61+
- name: Install Glfw Packages (MacOs)
62+
if: matrix.os == 'macos'
63+
shell: bash
64+
run: brew install glfw
4765

48-
- name: Prebuilt libs with shared link
66+
- name: PkgConfig build with static linking (MacOs)
67+
if: matrix.os == 'ubuntu' || matrix.os == 'macos'
4968
shell: bash
50-
run: cargo clean && cargo run -vv --example=version && cargo build -vv --examples
69+
run: cargo clean && cargo run -vv --example=version --features=static_link
5170

52-
# To check vulkan bindings.
53-
- name: Prepare Vulkan SDK
54-
uses: humbletim/[email protected]
55-
with:
56-
vulkan-query-version: latest
57-
vulkan-components: Vulkan-Headers
58-
vulkan-use-cache: true
71+
- name: PkgConfig build with shared linking (MacOs And Linux)
72+
if: matrix.os == 'ubuntu' || matrix.os == 'macos'
73+
shell: bash
74+
run: cargo clean && cargo run -vv --example=version
75+
5976

6077
# We don't pass --no-default-features, so, this generates bindings for
6178
# vulkan and native_gl/egl + other handles too by including system headers.
6279
- name: Generate Bindings
6380
shell: bash
64-
run: cargo clean && cargo run -vv --example=version --features=bindgen
81+
run: cargo clean && cargo run -vv --example=version --features=bindgen,src_build
6582

6683
# Just to make sure that the script works on all platforms.
6784
- name: Check gen_bindings.sh script

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ all = ["x11", "wayland", "native_handles", "native_gl", "native_egl", "vulkan"]
1919
bindgen = ["dep:bindgen"]
2020
# build from source, instead of using prebuilt libraries.
2121
src_build = ["dep:cmake"]
22+
prebuilt_libs = []
23+
2224
static_link = [] # static link (if on linux, src_build must also be enabled)
2325
vulkan = []
2426
wayland = []
@@ -34,6 +36,7 @@ osmesa = []
3436
[build-dependencies]
3537
bindgen = { version = "0.71", optional = true }
3638
cmake = { version = "0.1", optional = true }
39+
pkg-config = "0.3"
3740

3841
[dev-dependencies]
3942
glow = {version = "0.16"}

README.md

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,52 @@ This repo contains `glfw-sys` crate that provides FFI bindings to [glfw](https:/
55
## Design
66
This library has two main purposes:
77
1. provide FFI bindings to glfw: pre-generated (fast compile times) and build-time generation (slower).
8-
2. link to glfw library: source builds (using cmake) and [pre-built official glfw libs](https://github.com/glfw/glfw/releases).
8+
2. link to glfw library: system builds (using `pkg-config`), source builds (using `cmake`) and [pre-built official glfw libs](https://github.com/glfw/glfw/releases) (only for windows and mac).
99

1010

1111
For normal applications, you only need to care about 2 features:
1212
1. `src_build` - if you want to build from source. adds around 10 seconds of build time.
1313
2. `static_link` - if you want to link statically. On linux, this requires `src_build` too, so prefer dynamic linking during development for faster compile times.
1414

1515
### Features
16-
* `static_link` - statically link glfw. not available for linux if `src_build` is disabled.
17-
* `src_build` - build glfw from source. If disabled, we download pre-built glfw libs using system's curl + unzip/tar and link them.
18-
* `x11` and `wayland` - enables support for x11/wayland. Enable both and you can choose which one to use during initialization.
19-
* `vulkan` - enables some vulkan convenience functions (eg: surface creation).
20-
* `native_handles` - enable APIs to get platform specific window handles or display connections. useful for raw-window-handle support.
21-
* `native_gl` - enable APIs for getting platform specific gl contexts (wgl, egl, glx, nsgl etc..)
22-
* `native_egl` - enable egl API even for x11 builds
23-
* `osmesa` - I have no idea. Ignore this unless you know what you are doing.
24-
* `bindgen` - generate glfw FFI bindings at build time from headers. See [Below](#bindgen)
2516

26-
### Source Builds
27-
if `src_build` feature is enabled, we will build glfw from scratch.
28-
This requires `cmake` to be installed on your system and any other required dependencies.
17+
#### Building And Linking
18+
19+
> NOTE: For emscripten, none of these features apply. We just pass the necessary flags like `-sUSE_GLFW=3` to linker and simply let emscripten take care of things.
20+
21+
- `static_link` - statically link glfw. If disabled, we will dynamically link glfw.
22+
23+
We try to build glfw in this order:
24+
- `src_build` - If enabled, build glfw from source (sources are included with crate). Ensure `cmake` is installed and any other required dependencies.
25+
- `prebuilt_libs` (only for windows/macos. ignored on other platforms) - If enabled, we download and link pre-built glfw libs from <https://github.com/glfw/glfw/releases/>.
26+
27+
> NOTE: We use curl + tar (unzip on macos) to download and extract pre-built libs. mac/win10+ will have these by default.
2928
30-
### Prebuilt-libs builds
31-
If `src_build` feature is disabled, we will link with prebuilt glfw libraries.
32-
On windows and mac, we download the official libraries from <https://github.com/glfw/glfw/releases/>
33-
On linux, we expect user to have installed glfw library for linking (eg: `libglfw3-dev` on ubuntu). For static linking, you may probably require src_build, as most distros don't provide static libs.
29+
Finally, if neither `src_build` nor `prebuilt_libs` feature is enabled, we will try to use `pkg-config` to find and link to system glfw libs.
3430

35-
> NOTE: We use curl + tar (unzip on unix) to download and extract pre-built libs. mac/win10+/linux will have these by default.
31+
#### Platform Backends (non-mac and non-windows only)
32+
* `x11` and `wayland` - enables support for x11/wayland. Enable both and you can choose which one to use during initialization. `x11/wayland` are ignored on windows/macos platforms.
33+
34+
#### Vulkan
35+
- `vulkan` enables some vulkan convenience functions (eg: `glfwVulkanSupported`).
36+
- Only enable this if you need vulkan support.
37+
38+
#### Native Handles
39+
These features expose native "HWND"/"NSWindow"/"X11Connection" etc.. handles.
40+
Unless you are using `wgpu`-like libs that need raw-window-handles, these features can be ignored.
41+
- `native_handles` - enable APIs to get platform specific window handles or display connections or monitor ids. useful for raw-window-handle support.
42+
- `native_gl` - enable APIs for getting platform specific gl contexts (`wgl`, `egl`, `glx`, `nsgl` etc..). Most users should ignore this.
43+
- `native_egl` - enable egl API even for x11 builds, if you plan to use `egl` contexts with x11 windows. Most users should ignore this.
44+
45+
#### Miscellaneous
46+
* `osmesa` - I have no idea. Ignore this unless you know what you are doing.
47+
* `bindgen` - generate glfw FFI bindings at build time from headers. See [Below](#bindgen)
3648

3749

3850
### Pre-Generated bindings
3951
We generate FFI bindings at `src/sys/pregenerated.rs` and include them with the crate to keep the compile times fast. These are used when `bindgen` feature is disabled.
4052

41-
This generates core bindings, but skips platform specific bindings (eg: window handles or other platform specific API). Because generating them requires platform headers (eg: `windows.h`) and we can't provide headers for *all* platforms at once.
53+
This contains core bindings, but skips platform specific bindings (eg: window handles or other platform specific API). Because generating them requires platform headers (eg: `windows.h`) and we can't provide headers for *all* platforms at once.
4254

4355
So, platform specific bindings are manually maintained by hand in `src/sys/manual.rs`.
4456

@@ -52,5 +64,6 @@ These features will influence the bindings generated.
5264

5365
### Release Check List
5466
* When updating glfw version, make sure to checkout the submodule and commit it.
55-
* When updating glfw version, don't forget to change the url link build.rs to download the pre-built libs of the correct version.
67+
* When updating glfw version, don't forget to change the url link in build.rs to download the pre-built libs of the correct version.
68+
* When updating glfw version, don't forget to update the pkg-config `atleast_version` argument.
5669
* Check that the bindings generated are the same on all platforms by checking the CI logs for the `gen_bindings.sh` step.

build.rs

Lines changed: 103 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,44 +21,88 @@ fn main() {
2121
#[cfg(feature = "bindgen")]
2222
generate_bindings(features, &out_dir);
2323

24-
// only for mac/windows.
25-
// other platforms like linux/bsd should install their platform libraries
26-
// using system package manager.
27-
//
28-
// if other platforms require static builds, you may use src builds instead.
29-
#[cfg(not(feature = "src_build"))]
30-
download_libs(features, &out_dir);
31-
32-
// build from src, instead of using prebuilt-libraries.
33-
#[cfg(feature = "src_build")]
34-
build_from_src(features, &out_dir);
24+
// lets special case emscripten and early return.
25+
if features.os == TargetOs::Emscripten {
26+
// tell emscripten to expose glfw bindings
27+
println!("cargo:rustc-link-arg=-sUSE_GLFW=3");
28+
// Without this, we get errors like
29+
// = note: wasm-ld: error: .../basic.diqs9uv01tyf3yxt0iu6v8zc8.rcgu.o: undefined symbol: glfwGetError
30+
println!("cargo:rustc-link-arg=-sERROR_ON_UNDEFINED_SYMBOLS=0");
31+
return;
32+
}
3533

36-
// emit the linker flags
37-
// requires src_build if not win/mac os, as most distros only provide shared libs.
38-
if features.static_link {
39-
println!("cargo:rustc-link-lib=static=glfw3");
34+
// not src build and not prebuilt_libs => use pkg-config
35+
let pkgconfig_build = !features.src_build && !features.prebuilt_libs;
36+
if features.src_build {
37+
// build from src, instead of using prebuilt-libraries. ignore on emscripten target.
38+
#[cfg(feature = "src_build")]
39+
build_from_src(features, &out_dir);
40+
} else if features.prebuilt_libs {
41+
download_libs(features, &out_dir);
4042
} else {
43+
assert!(pkgconfig_build);
44+
// emits linker flags by default.
45+
match pkg_config::Config::new()
46+
.statik(features.static_link)
47+
.atleast_version("3.4.0")
48+
.probe("glfw3")
49+
{
50+
Ok(lib) => println!("pkg-config found glfw library {lib:#?}"),
51+
Err(e) => panic!("pkg-config failed to find glfw library: {e}"),
52+
}
53+
}
54+
55+
// pkg-config takes care of emitting linker flags, so we only explicitly
56+
// need to emit them if we aren't using pkg-config.
57+
if !pkgconfig_build {
58+
if features.static_link {
59+
println!("cargo:rustc-link-lib=static=glfw3");
60+
} else {
61+
match features.os {
62+
TargetOs::Win => println!("cargo:rustc-link-lib=dylib=glfw3dll"),
63+
_ => println!("cargo:rustc-link-lib=dylib=glfw"),
64+
}
65+
}
66+
}
67+
68+
// First, we link system libs recommended by official glfw docs
69+
// from glfw/src/CmakeLists.txt - glfw_PKG_LIBS
70+
// and https://www.glfw.org/docs/latest/build_guide.html
71+
72+
// pkg-config builds will already emit these flags, so we only
73+
// need to emit them if we aren't using pkg-config
74+
if !pkgconfig_build {
4175
match features.os {
42-
TargetOs::Win => println!("cargo:rustc-link-lib=dylib=glfw3dll"),
43-
_ => println!("cargo:rustc-link-lib=dylib=glfw"),
76+
TargetOs::Win => {
77+
println!("cargo:rustc-link-lib=gdi32");
78+
}
79+
TargetOs::Mac => {
80+
println!("cargo:rustc-link-lib=framework=Cocoa");
81+
println!("cargo:rustc-link-lib=framework=IOKit");
82+
println!("cargo:rustc-link-lib=framework=CoreFoundation");
83+
}
84+
_ => {}
4485
}
4586
}
46-
// extra libraries for win/mac
87+
// next, we link extra libs based on glfw-rs/src/ffi/links.rs
88+
// TODO: check if we actually need these.
4789
match features.os {
4890
TargetOs::Win => {
49-
println!("cargo:rustc-link-lib=dylib=gdi32");
50-
println!("cargo:rustc-link-lib=dylib=user32");
51-
println!("cargo:rustc-link-lib=dylib=kernel32");
52-
println!("cargo:rustc-link-lib=dylib=shell32");
91+
println!("cargo:rustc-link-lib=opengl32");
92+
println!("cargo:rustc-link-lib=user32");
93+
println!("cargo:rustc-link-lib=shell32");
5394
}
5495
TargetOs::Mac => {
55-
println!("cargo:rustc-link-lib=framework=Cocoa");
56-
println!("cargo:rustc-link-lib=framework=IOKit");
57-
println!("cargo:rustc-link-lib=framework=CoreFoundation");
96+
println!("cargo:rustc-link-lib=framework=OpenGL");
5897
println!("cargo:rustc-link-lib=framework=QuartzCore");
5998
}
60-
TargetOs::Linux => {
61-
// Gl? idk.
99+
TargetOs::Linux | TargetOs::Others => {
100+
if features.x11 {
101+
println!("cargo:rustc-link-lib=X11");
102+
}
103+
if features.wayland {
104+
println!("cargo:rustc-link-lib=wayland-client");
105+
}
62106
}
63107
_ => {}
64108
}
@@ -70,6 +114,7 @@ enum TargetOs {
70114
Win,
71115
Mac,
72116
Linux,
117+
Emscripten,
73118
Others,
74119
}
75120
/// The features enabled for this build
@@ -102,34 +147,46 @@ struct Features {
102147
/// generate bindings for native gl bindings like wgl, glx, nsgl, egl etc..
103148
/// For X11, you can explicitly enable egl-related functionality using `egl` feature.
104149
gl: bool,
150+
/// whether we are doing a src build
151+
src_build: bool,
152+
/// whether we are using prebuilt libs
153+
/// This is only true if feature is enabled AND target is win/mac
154+
prebuilt_libs: bool,
105155
}
106156
/// Use `cfg` macro to get the selected features.
107157
impl Default for Features {
108158
fn default() -> Self {
159+
let os = match std::env::var("CARGO_CFG_TARGET_OS")
160+
.expect("failed to get target os")
161+
.as_str()
162+
{
163+
"windows" => TargetOs::Win,
164+
"macos" => TargetOs::Mac,
165+
"linux" => TargetOs::Linux,
166+
"emscripten" => TargetOs::Emscripten,
167+
_ => TargetOs::Others,
168+
};
109169
Self {
110170
static_link: cfg!(feature = "static_link"),
111171
vulkan: cfg!(feature = "vulkan"),
112172
native: cfg!(feature = "native_handles"),
113-
os: match std::env::var("CARGO_CFG_TARGET_OS")
114-
.expect("failed to get target os")
115-
.as_str()
116-
{
117-
"windows" => TargetOs::Win,
118-
"macos" => TargetOs::Mac,
119-
"linux" => TargetOs::Linux,
120-
_ => TargetOs::Others,
121-
},
173+
os,
122174
wayland: cfg!(feature = "wayland"),
123175
x11: cfg!(feature = "x11"),
124176
egl: cfg!(feature = "native_egl"),
125177
osmesa: cfg!(feature = "osmesa"),
126178
bindgen: cfg!(feature = "bindgen"),
127179
gl: cfg!(feature = "native_gl"),
180+
src_build: cfg!(feature = "src_build"),
181+
// this feature only works on windows and mac
182+
prebuilt_libs: cfg!(feature = "prebuilt_libs")
183+
&& (os == TargetOs::Win || os == TargetOs::Mac),
128184
}
129185
}
130186
}
131187
/// builds from source using cmake.
132188
/// The sources are included with this crate.
189+
/// feature-gated to make cmake crate optional.
133190
#[cfg(feature = "src_build")]
134191
fn build_from_src(features: Features, _out_dir: &str) {
135192
let mut config = cmake::Config::new("./glfw");
@@ -169,6 +226,7 @@ fn build_from_src(features: Features, _out_dir: &str) {
169226
}
170227

171228
/// Generates bindings using bindgen
229+
/// feature-gated to make bindgen crate optional
172230
#[cfg(feature = "bindgen")]
173231
fn generate_bindings(features: Features, out_dir: &str) {
174232
assert!(features.bindgen); // sanity check
@@ -266,21 +324,26 @@ fn generate_bindings(features: Features, out_dir: &str) {
266324
for item in DUPLICATE_ITEMS {
267325
bindings = bindings.blocklist_item(item);
268326
}
327+
// workaround for emscripten - https://github.com/rust-lang/rust-bindgen/issues/1941
328+
if features.os == TargetOs::Emscripten {
329+
bindings = bindings.clang_arg("-fvisibility=default");
330+
}
269331
// finally!
270-
bindings
332+
bindings = bindings
271333
.merge_extern_blocks(true)
272334
// default is "u32", but glfw uses i32 in its API.
273335
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
274336
// we only care about items from glfw3 header
275337
// This is the name of the header we gave for our made-up header above where we merged everything.
276-
.allowlist_file(".*glfw3\\.h")
338+
.allowlist_file(".*glfw3\\.h");
339+
println!("bindgen final config: {:#?}", bindings);
340+
bindings
277341
.generate()
278342
.expect("failed to generate bindings")
279343
.write_to_file(format!("{out_dir}/bindings.rs"))
280344
.expect("failed to write bindings to out_dir/bindings.rs");
281345
}
282346
/// Download prebuilt libraries
283-
#[cfg(not(feature = "src_build"))]
284347
fn download_libs(features: Features, out_dir: &str) {
285348
const URL: &str = "https://github.com/glfw/glfw/releases/download/3.4";
286349
let zip_name: &str = match features.os {
@@ -295,7 +358,7 @@ fn download_libs(features: Features, out_dir: &str) {
295358
}
296359
TargetOs::Mac => "glfw-3.4.bin.MACOS",
297360
_ => {
298-
return;
361+
unimplemented!("prebuilt libs not available for this OS");
299362
}
300363
};
301364
let url = format!("{}/{}.zip", URL, zip_name);

0 commit comments

Comments
 (0)