• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 std::env;
6 use std::fs;
7 use std::fs::File;
8 use std::io::Write;
9 use std::path::Path;
10 use std::path::PathBuf;
11 use std::process::Command;
12 
13 use anyhow::anyhow;
14 use anyhow::bail;
15 use anyhow::Context;
16 use anyhow::Result;
17 
is_native_build() -> bool18 fn is_native_build() -> bool {
19     env::var("HOST").unwrap() == env::var("TARGET").unwrap()
20 }
21 
use_system_minigbm() -> bool22 fn use_system_minigbm() -> bool {
23     println!("cargo:rerun-if-env-changed=CROSVM_BUILD_VARIANT");
24     println!("cargo:rerun-if-env-changed=CROSVM_USE_SYSTEM_MINIGBM");
25     env::var("CROSVM_BUILD_VARIANT").unwrap_or_default() == "chromeos"
26         || env::var("CROSVM_USE_SYSTEM_MINIGBM").unwrap_or_else(|_| "0".to_string()) != "0"
27 }
28 
use_system_virglrenderer() -> bool29 fn use_system_virglrenderer() -> bool {
30     println!("cargo:rerun-if-env-changed=CROSVM_BUILD_VARIANT");
31     println!("cargo:rerun-if-env-changed=CROSVM_USE_SYSTEM_VIRGLRENDERER");
32     env::var("CROSVM_BUILD_VARIANT").unwrap_or_default() == "chromeos"
33         || env::var("CROSVM_USE_SYSTEM_VIRGLRENDERER").unwrap_or_else(|_| "0".to_string()) != "0"
34 }
35 
36 /// Returns the target triplet prefix for gcc commands. No prefix is required
37 /// for native builds.
get_cross_compile_prefix() -> String38 fn get_cross_compile_prefix() -> String {
39     if is_native_build() {
40         return String::from("");
41     }
42 
43     let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
44     let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
45     let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
46     format!("{}-{}-{}-", arch, os, env)
47 }
48 
49 /// For cross-compilation with meson, we need to pick a cross-file, which
50 /// live in /usr/local/share/meson/cross.
get_meson_cross_args() -> Vec<String>51 fn get_meson_cross_args() -> Vec<String> {
52     if is_native_build() {
53         Vec::new()
54     } else {
55         vec![
56             "--cross-file".to_string(),
57             env::var("CARGO_CFG_TARGET_ARCH").unwrap(),
58         ]
59     }
60 }
61 
env_prepend_pkg_config_path(new_path: &Path) -> Result<()>62 fn env_prepend_pkg_config_path(new_path: &Path) -> Result<()> {
63     const KEY: &str = "PKG_CONFIG_PATH";
64     let new_path_string = new_path
65         .to_str()
66         .ok_or(anyhow!("failed to convert path to string"))?;
67     if let Ok(original_value) = env::var(KEY) {
68         env::set_var(KEY, format!("{}:{}", new_path_string, original_value));
69     } else {
70         env::set_var(KEY, new_path);
71     };
72     Ok(())
73 }
74 
75 /// Builds from pinned commit as static library and probes the generated pkgconfig file to emit
76 /// cargo linking metadata
build_and_probe_minigbm(out_dir: &Path) -> Result<()>77 fn build_and_probe_minigbm(out_dir: &Path) -> Result<()> {
78     const SOURCE_DIR: &str = "../third_party/minigbm";
79     let pkgconfig_file = out_dir.join("gbm.pc");
80 
81     println!("cargo:rerun-if-changed={}", SOURCE_DIR);
82 
83     if !Path::new(SOURCE_DIR).join(".git").exists() {
84         bail!(
85             "{} source does not exist, did you forget to \
86             `git submodule update --init`?",
87             SOURCE_DIR
88         );
89     }
90 
91     // build static library
92     let make_flags = env::var("CARGO_MAKEFLAGS").unwrap();
93     let status = Command::new("make")
94         .env("MAKEFLAGS", make_flags)
95         .env("VERBOSE", "1")
96         .env("CROSS_COMPILE", get_cross_compile_prefix())
97         .arg(format!("OUT={}", out_dir.display()))
98         .arg("CC_STATIC_LIBRARY(libminigbm.pie.a)")
99         .current_dir(SOURCE_DIR)
100         .status()?;
101     if !status.success() {
102         bail!("make failed with status: {}", status);
103     }
104 
105     // copy headers to build output
106     let src_dir = Path::new(SOURCE_DIR);
107     fs::copy(src_dir.join("gbm.h"), out_dir.join("gbm.h"))?;
108     fs::copy(
109         src_dir.join("minigbm_helpers.h"),
110         out_dir.join("minigbm_helpers.h"),
111     )?;
112 
113     // minigbm will be linked using the name gbm, make sure it can be found.
114     fs::copy(out_dir.join("libminigbm.pie.a"), out_dir.join("libgbm.a"))?;
115 
116     // write out a custom pkgconfig
117     let mut conf = File::create(pkgconfig_file)?;
118     let contents = format!(
119         r#"prefix={install_dir}
120 includedir=${{prefix}}
121 libdir=${{prefix}}
122 
123 Name: libgbm
124 Description: A small gbm implementation
125 Version: 18.0.0
126 Cflags: -I${{includedir}}
127 Libs: -L${{libdir}} -lgbm
128 Requires.private: libdrm >= 2.4.50
129 "#,
130         install_dir = out_dir.display()
131     );
132     conf.write_all(contents.as_bytes())?;
133 
134     // let pkg_config crate configure the cargo link metadata according to the custom pkgconfig
135     // above
136     env_prepend_pkg_config_path(out_dir)?;
137     let mut config = pkg_config::Config::new();
138     config.statik(true).probe("gbm")?;
139     Ok(())
140 }
141 
minigbm() -> Result<()>142 fn minigbm() -> Result<()> {
143     if use_system_minigbm() {
144         pkg_config::probe_library("gbm").context("pkgconfig failed to find gbm")?;
145     } else {
146         // Otherwise build from source and emit cargo build metadata
147         let out_dir = PathBuf::from(env::var("OUT_DIR")?).join("minigbm");
148         build_and_probe_minigbm(&out_dir).context("failed building minigbm")?;
149     };
150     Ok(())
151 }
152 
153 /// Builds from pinned commit as static library and probes the generated pkgconfig file to emit
154 /// cargo linking metadata
build_and_probe_virglrenderer(out_dir: &Path) -> Result<()>155 fn build_and_probe_virglrenderer(out_dir: &Path) -> Result<()> {
156     const SOURCE_DIR: &str = "../third_party/virglrenderer";
157     let install_prefix = out_dir.join("installed");
158 
159     println!("cargo:rerun-if-changed={}", SOURCE_DIR);
160 
161     if !Path::new(SOURCE_DIR).join(".git").exists() {
162         bail!(
163             "{} source does not exist, did you forget to \
164                 `git submodule update --init`?",
165             SOURCE_DIR
166         );
167     }
168 
169     let mut platforms = vec!["egl"];
170     if env::var("CARGO_FEATURE_X").is_ok() {
171         platforms.push("glx");
172     }
173 
174     // Ensures minigbm is available and that it's pkgconfig is locatable
175     minigbm()?;
176 
177     let mut setup = Command::new("meson");
178     setup
179         .arg("setup")
180         .current_dir(SOURCE_DIR)
181         .arg("--prefix")
182         .arg(install_prefix.as_os_str())
183         .arg("--libdir")
184         .arg("lib")
185         .args(get_meson_cross_args())
186         .arg(format!("-Dplatforms={}", platforms.join(",")))
187         .arg("-Ddefault_library=static")
188         .arg(out_dir.as_os_str());
189 
190     let setup_status = setup.status()?;
191     if !setup_status.success() {
192         bail!("meson setup failed with status: {}", setup_status);
193     }
194 
195     let mut compile = Command::new("meson");
196     compile
197         .arg("compile")
198         .arg("src/virglrenderer")
199         .current_dir(out_dir);
200     let compile_status = compile.status()?;
201     if !compile_status.success() {
202         bail!("meson compile failed with status: {}", compile_status);
203     }
204 
205     let mut install = Command::new("meson");
206     install.arg("install").current_dir(out_dir);
207     let install_status = install.status()?;
208     if !install_status.success() {
209         bail!("meson install failed with status: {}", install_status);
210     }
211 
212     let pkg_config_path = install_prefix.join("lib/pkgconfig");
213     assert!(pkg_config_path.join("virglrenderer.pc").exists());
214 
215     // let pkg_config crate configure the cargo link metadata according to the generated pkgconfig
216     env_prepend_pkg_config_path(pkg_config_path.as_path())?;
217     let mut config = pkg_config::Config::new();
218     config.statik(true).probe("virglrenderer")?;
219 
220     Ok(())
221 }
222 
virglrenderer() -> Result<()>223 fn virglrenderer() -> Result<()> {
224     if use_system_virglrenderer() && !use_system_minigbm() {
225         bail!("Must use system minigbm if using system virglrenderer (try setting CROSVM_USE_SYSTEM_MINIGBM=1)");
226     }
227 
228     // Use virglrenderer package from pkgconfig on ChromeOS builds
229     if use_system_virglrenderer() {
230         let lib = pkg_config::Config::new()
231             .atleast_version("1.0.0")
232             .probe("virglrenderer")
233             .context("pkgconfig failed to find virglrenderer")?;
234         if lib.defines.contains_key("VIRGL_RENDERER_UNSTABLE_APIS") {
235             println!("cargo:rustc-cfg=virgl_renderer_unstable");
236         }
237     } else {
238         // Otherwise build from source.
239         let out_dir = PathBuf::from(env::var("OUT_DIR")?).join("virglrenderer");
240         build_and_probe_virglrenderer(&out_dir)?;
241     }
242     Ok(())
243 }
244 
gfxstream() -> Result<()>245 fn gfxstream() -> Result<()> {
246     if let Ok(gfxstream_path) = env::var("GFXSTREAM_PATH") {
247         println!("cargo:rustc-link-lib=gfxstream_backend");
248         println!("cargo:rustc-link-search={}", gfxstream_path);
249         Ok(())
250     } else {
251         let gfxstream_lib = pkg_config::Config::new().probe("gfxstream_backend")?;
252 
253         if gfxstream_lib.defines.contains_key("GFXSTREAM_UNSTABLE") {
254             println!("cargo:rustc-cfg=gfxstream_unstable");
255         }
256 
257         pkg_config::Config::new().probe("aemu_base")?;
258         pkg_config::Config::new().probe("aemu_host_common")?;
259         pkg_config::Config::new().probe("aemu_logging")?;
260         pkg_config::Config::new().probe("aemu_snapshot")?;
261 
262         let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
263 
264         if target_os.contains("linux") {
265             pkg_config::Config::new().probe("libdrm")?;
266         }
267 
268         // Need to link against libc++ or libstdc++.  Apple is clang-only, while by default other
269         // Unix platforms use libstdc++.
270         if target_os.contains("macos") {
271             println!("cargo:rustc-link-lib=dylib=c++");
272         } else if target_os.contains("linux") || target_os.contains("nto") {
273             println!("cargo:rustc-link-lib=dylib=stdc++");
274         }
275 
276         Ok(())
277     }
278 }
279 
main() -> Result<()>280 fn main() -> Result<()> {
281     // Skip installing dependencies when generating documents.
282     if env::var("CARGO_DOC").is_ok() {
283         return Ok(());
284     }
285 
286     if env::var("CARGO_FEATURE_MINIGBM").is_ok() {
287         minigbm()?;
288     }
289 
290     if env::var("CARGO_FEATURE_VIRGL_RENDERER").is_ok() {
291         virglrenderer()?;
292     }
293 
294     if env::var("CARGO_FEATURE_GFXSTREAM").is_ok()
295         && env::var("CARGO_FEATURE_GFXSTREAM_STUB").is_err()
296     {
297         gfxstream()?;
298     }
299 
300     Ok(())
301 }
302