|
| 1 | +// Copyright 2022 The ChromiumOS Authors |
| 2 | +// Use of this source code is governed by a BSD-style license that can be |
| 3 | +// found in the LICENSE file. |
| 4 | + |
| 5 | +// Vendored verbatim from cros-libva (BSD-3-Clause); not subject to strict lints. |
| 6 | +#![allow(clippy::all)] |
| 7 | + |
| 8 | +use regex::Regex; |
| 9 | +use std::env::{self}; |
| 10 | +use std::fs::read_to_string; |
| 11 | +use std::path::{Path, PathBuf}; |
| 12 | + |
| 13 | +mod bindgen_gen; |
| 14 | +use bindgen_gen::vaapi_gen_builder; |
| 15 | + |
| 16 | +/// Wrapper file to use as input of bindgen. |
| 17 | +const WRAPPER_PATH: &str = "libva-wrapper.h"; |
| 18 | + |
| 19 | +/// Generate `va_version.h` from the vendored `va_version.h.in` template by |
| 20 | +/// extracting version numbers from `meson.build`. The `libva/` headers are |
| 21 | +/// checked into the repo and refreshed by `just vendor`. |
| 22 | +fn generate_vendored_version_header(out_dir: &Path) -> (u32, u32) { |
| 23 | + let meson_build = |
| 24 | + read_to_string("libva/meson.build").expect("failed to read libva/meson.build — run `just vendor`"); |
| 25 | + |
| 26 | + // Extract va_api_{major,minor,micro}_version from meson.build |
| 27 | + let extract = |var_name: &str| -> String { |
| 28 | + let re = Regex::new(&format!(r"{}\s*=\s*(\d+)", var_name)).unwrap(); |
| 29 | + re.captures(&meson_build) |
| 30 | + .unwrap_or_else(|| panic!("{} not found in libva/meson.build", var_name))[1] |
| 31 | + .to_string() |
| 32 | + }; |
| 33 | + |
| 34 | + let major = extract("va_api_major_version"); |
| 35 | + let minor = extract("va_api_minor_version"); |
| 36 | + let micro = extract("va_api_micro_version"); |
| 37 | + let version = format!("{}.{}.{}", major, minor, micro); |
| 38 | + |
| 39 | + let template = read_to_string("libva/va/va_version.h.in").expect("failed to read libva/va/va_version.h.in"); |
| 40 | + |
| 41 | + let generated = template |
| 42 | + .replace("@VA_API_MAJOR_VERSION@", &major) |
| 43 | + .replace("@VA_API_MINOR_VERSION@", &minor) |
| 44 | + .replace("@VA_API_MICRO_VERSION@", µ) |
| 45 | + .replace("@VA_API_VERSION@", &version); |
| 46 | + |
| 47 | + let va_dir = out_dir.join("va"); |
| 48 | + std::fs::create_dir_all(&va_dir).expect("failed to create va dir in OUT_DIR"); |
| 49 | + std::fs::write(va_dir.join("va_version.h"), generated).expect("failed to write va_version.h"); |
| 50 | + |
| 51 | + // libva keeps va_drm.h under va/drm/ in its source tree, but installs it (and |
| 52 | + // the wrapper includes it) as <va/va_drm.h>. Stage it next to the generated |
| 53 | + // va_version.h so the OUT_DIR include path resolves <va/va_drm.h>. |
| 54 | + std::fs::copy("libva/va/drm/va_drm.h", va_dir.join("va_drm.h")).expect("failed to stage va/drm/va_drm.h"); |
| 55 | + |
| 56 | + ( |
| 57 | + major.parse().expect("invalid major version"), |
| 58 | + minor.parse().expect("invalid minor version"), |
| 59 | + ) |
| 60 | +} |
| 61 | + |
| 62 | +fn main() { |
| 63 | + // Do not require dependencies when generating docs. |
| 64 | + if std::env::var("CARGO_DOC").is_ok() || std::env::var("DOCS_RS").is_ok() { |
| 65 | + return; |
| 66 | + } |
| 67 | + |
| 68 | + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("`OUT_DIR` is not set")); |
| 69 | + |
| 70 | + // Always build against the vendored libva headers (checked into `libva/`, |
| 71 | + // pinned to a known VA-API version) and dlopen libva at runtime rather than |
| 72 | + // link it. There is no system-libva path, so the build needs only libclang for |
| 73 | + // bindgen and works on any OS. |
| 74 | + let (major, minor) = generate_vendored_version_header(&out_dir); |
| 75 | + println!("cargo:rerun-if-changed=libva/meson.build"); |
| 76 | + println!("cargo:rerun-if-changed=libva/va/va_version.h.in"); |
| 77 | + |
| 78 | + // Two include paths: the vendored `libva/` root so `#include <va/va.h>` |
| 79 | + // resolves to `libva/va/va.h`, and OUT_DIR for the generated |
| 80 | + // `<va/va_version.h>` and the staged `<va/va_drm.h>`. |
| 81 | + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is not set")); |
| 82 | + let va_h_path = manifest_dir.join("libva"); |
| 83 | + assert!( |
| 84 | + va_h_path.join("meson.build").exists(), |
| 85 | + "{} is missing the vendored libva headers — run `just vendor`", |
| 86 | + va_h_path.display() |
| 87 | + ); |
| 88 | + |
| 89 | + println!("libva {}.{} is used to generate bindings", major, minor); |
| 90 | + let va_check_version = |desired_major: u32, desired_minor: u32| { |
| 91 | + major > desired_major || (major == desired_major && minor >= desired_minor) |
| 92 | + }; |
| 93 | + |
| 94 | + // Declare the custom cfg flags to avoid warnings |
| 95 | + println!("cargo::rustc-check-cfg=cfg(libva_1_21_or_higher)"); |
| 96 | + println!("cargo::rustc-check-cfg=cfg(libva_1_20_or_higher)"); |
| 97 | + println!("cargo::rustc-check-cfg=cfg(libva_1_19_or_higher)"); |
| 98 | + println!("cargo::rustc-check-cfg=cfg(libva_1_16_or_higher)"); |
| 99 | + println!("cargo::rustc-check-cfg=cfg(libva_1_15_or_higher)"); |
| 100 | + println!("cargo::rustc-check-cfg=cfg(libva_1_14_or_higher)"); |
| 101 | + println!("cargo::rustc-check-cfg=cfg(libva_1_10_or_higher)"); |
| 102 | + |
| 103 | + // Set the cfg flags based on version |
| 104 | + if va_check_version(1, 21) { |
| 105 | + println!("cargo::rustc-cfg=libva_1_21_or_higher"); |
| 106 | + } |
| 107 | + if va_check_version(1, 20) { |
| 108 | + println!("cargo::rustc-cfg=libva_1_20_or_higher") |
| 109 | + } |
| 110 | + if va_check_version(1, 19) { |
| 111 | + println!("cargo::rustc-cfg=libva_1_19_or_higher") |
| 112 | + } |
| 113 | + if va_check_version(1, 16) { |
| 114 | + println!("cargo::rustc-cfg=libva_1_16_or_higher") |
| 115 | + } |
| 116 | + if va_check_version(1, 15) { |
| 117 | + println!("cargo::rustc-cfg=libva_1_15_or_higher"); |
| 118 | + } |
| 119 | + if va_check_version(1, 14) { |
| 120 | + println!("cargo::rustc-cfg=libva_1_14_or_higher"); |
| 121 | + } |
| 122 | + if va_check_version(1, 10) { |
| 123 | + println!("cargo::rustc-cfg=libva_1_10_or_higher"); |
| 124 | + } |
| 125 | + |
| 126 | + let bindings = vaapi_gen_builder(bindgen::builder()) |
| 127 | + .header(WRAPPER_PATH) |
| 128 | + .clang_arg(format!("-I{}", va_h_path.display())) |
| 129 | + .clang_arg(format!("-I{}", out_dir.display())) |
| 130 | + .generate() |
| 131 | + .expect("unable to generate bindings"); |
| 132 | + |
| 133 | + bindings |
| 134 | + .write_to_file(out_dir.join("bindings.rs")) |
| 135 | + .expect("Couldn't write bindings!"); |
| 136 | +} |
0 commit comments