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 use regex::Regex;
6 use std::env::VarError;
7 use std::env::{self};
8 use std::fs::read_to_string;
9 use std::path::{Path, PathBuf};
10
11 mod bindgen_gen;
12 use bindgen_gen::vaapi_gen_builder;
13
14 /// Environment variable that can be set to point to the directory containing the `va.h`, `va_drm.h` and `va_drmcommon.h`
15 /// files to use to generate the bindings.
16 const CROS_LIBVA_H_PATH_ENV: &str = "CROS_LIBVA_H_PATH";
17 const CROS_LIBVA_LIB_PATH_ENV: &str = "CROS_LIBVA_LIB_PATH";
18
19 /// Wrapper file to use as input of bindgen.
20 const WRAPPER_PATH: &str = "libva-wrapper.h";
21
22 // Return VA_MAJOR_VERSION and VA_MINOR_VERSION from va_version.h.
get_va_version(va_h_path: &str) -> (u32, u32)23 fn get_va_version(va_h_path: &str) -> (u32, u32) {
24 let va_version_h_path = Path::new(va_h_path).join("va/va_version.h");
25 assert!(
26 va_version_h_path.exists(),
27 "{} doesn't exist",
28 va_version_h_path.display()
29 );
30 let header_content = read_to_string(va_version_h_path).unwrap();
31 let lines = header_content.lines();
32
33 const VERSION_REGEX_STRINGS: [&str; 2] = [
34 r"#define VA_MAJOR_VERSION\s*[0-9]+",
35 r"#define VA_MINOR_VERSION\s*[0-9]+",
36 ];
37 let mut numbers: [u32; 2] = [0; 2];
38 for i in 0..2 {
39 let re = Regex::new(VERSION_REGEX_STRINGS[i]).unwrap();
40 let match_line = lines
41 .clone()
42 .filter(|&s| re.is_match(s))
43 .collect::<Vec<_>>();
44 assert_eq!(
45 match_line.len(),
46 1,
47 "unexpected match for {}: {:?}",
48 VERSION_REGEX_STRINGS[i],
49 match_line
50 );
51 let number_str = Regex::new(r"[0-9]+")
52 .unwrap()
53 .find(match_line[0])
54 .unwrap()
55 .as_str();
56 numbers[i] = number_str.parse::<u32>().unwrap();
57 }
58
59 (numbers[0], numbers[1])
60 }
61
main()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 va_h_path = env::var(CROS_LIBVA_H_PATH_ENV)
69 .or_else(|e| {
70 if let VarError::NotPresent = e {
71 let libva_library = pkg_config::probe_library("libva");
72 match libva_library {
73 Ok(_) => Ok(libva_library.unwrap().include_paths[0]
74 .clone()
75 .into_os_string()
76 .into_string()
77 .unwrap()),
78 Err(e) => panic!("libva is not found in system: {}", e),
79 }
80 } else {
81 Err(e)
82 }
83 })
84 .expect("libva header location is unknown");
85
86 let va_lib_path = env::var(CROS_LIBVA_LIB_PATH_ENV).unwrap_or_default();
87 // Check the path exists.
88 if !va_h_path.is_empty() {
89 assert!(
90 Path::new(&va_h_path).exists(),
91 "{} doesn't exist",
92 va_h_path
93 );
94 }
95
96 let (major, minor) = get_va_version(&va_h_path);
97 println!("libva {}.{} is used to generate bindings", major, minor);
98 let va_check_version = |desired_major: u32, desired_minor: u32| {
99 major > desired_major || (major == desired_major && minor >= desired_minor)
100 };
101
102 if va_check_version(1, 21) {
103 println!("cargo::rustc-cfg=libva_1_21_or_higher");
104 }
105 if va_check_version(1, 20) {
106 println!("cargo::rustc-cfg=libva_1_20_or_higher")
107 }
108 if va_check_version(1, 19) {
109 println!("cargo::rustc-cfg=libva_1_19_or_higher")
110 }
111 if va_check_version(1, 16) {
112 println!("cargo::rustc-cfg=libva_1_16_or_higher")
113 }
114
115 if !va_lib_path.is_empty() {
116 assert!(
117 Path::new(&va_lib_path).exists(),
118 "{} doesn't exist",
119 va_lib_path
120 );
121 println!("cargo:rustc-link-arg=-Wl,-rpath={}", va_lib_path);
122 }
123
124 // Tell cargo to link va and va-drm objects dynamically.
125 println!("cargo:rustc-link-lib=dylib=va");
126 println!("cargo:rustc-link-lib=dylib=va-drm"); // for the vaGetDisplayDRM entrypoint
127
128 let mut bindings_builder = vaapi_gen_builder(bindgen::builder()).header(WRAPPER_PATH);
129 if !va_h_path.is_empty() {
130 bindings_builder = bindings_builder.clang_arg(format!("-I{}", va_h_path));
131 }
132 let bindings = bindings_builder
133 .generate()
134 .expect("unable to generate bindings");
135
136 let out_path = PathBuf::from(env::var("OUT_DIR").expect("`OUT_DIR` is not set"));
137
138 bindings
139 .write_to_file(out_path.join("bindings.rs"))
140 .expect("Couldn't write bindings!");
141 }
142