include_graph/dependencies/
gn.rs1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4};
5
6use serde::Deserialize;
7use tokio::process::Command;
8use tracing::{error, info};
9
10use super::error::Error;
11
12#[derive(Debug, PartialEq)]
13pub struct GnTarget {
14 pub name: String,
15 pub sources: Vec<PathBuf>,
16}
17
18#[derive(Deserialize)]
19#[serde(transparent)]
20struct GnSourcesOutput {
21 inner: HashMap<String, SourcesData>,
22}
23
24#[derive(Deserialize)]
25struct SourcesData {
26 sources: Option<Vec<String>>,
27}
28
29pub async fn load_gn_targets(
30 gn_dir: &Path,
31 source_root: &Path,
32 target: &str,
33) -> Result<Vec<GnTarget>, Error> {
34 let mut command = Command::new("/usr/bin/gn");
36 command.arg("desc");
37 command.arg("--format=json");
38 command.arg(format!(
39 "--root={}",
40 source_root
41 .canonicalize()
42 .map_err(|e| Error::Internal {
43 message: format!("Canonical path: {:?}", e),
44 })?
45 .to_string_lossy(),
46 ));
47 command.arg(gn_dir.canonicalize().map_err(|e| Error::Internal {
48 message: format!("Canonical path: {:?}", e),
49 })?);
50 command.arg(target);
51 command.arg("sources");
52
53 let output = command.output().await.map_err(|e| Error::Internal {
54 message: format!("Canonical path: {:?}", e),
55 })?;
56
57 if !output.status.success() {
58 let data = String::from_utf8_lossy(&output.stdout);
59 if data.len() > 0 {
60 for l in data.lines() {
61 error!("STDOUT: {}", l);
62 }
63 }
64
65 let data = String::from_utf8_lossy(&output.stderr);
66 if data.len() > 0 {
67 for l in data.lines() {
68 error!("STDERR: {}", l);
69 }
70 }
71
72 return Err(Error::Internal {
73 message: format!("Failed to execute GN. Status {:?}.", output.status),
74 });
75 }
76
77 let decoded: GnSourcesOutput =
78 serde_json::from_slice(&output.stdout).map_err(|e| Error::Internal {
79 message: format!("JSON parse error: {:?}", e),
80 })?;
81
82 Ok(decoded
85 .inner
86 .into_iter()
87 .filter_map(|(name, sources)| {
88 info!(target: "gn-path", "Sources for {}", &name);
89 sources.sources.map(|sources| GnTarget {
90 name,
91 sources: sources
92 .into_iter()
93 .filter_map(|s| {
94 if s.starts_with("//") {
95 let mut path = PathBuf::from(source_root);
97 path.push(PathBuf::from(&s.as_str()[2..]));
98 path
99 } else {
100 PathBuf::from(&s.as_str())
102 }
103 .canonicalize()
104 .ok()
105 })
106 .inspect(|path| {
107 info!(target: "gn-path", " - {:?}", path);
108 })
109 .collect(),
110 })
111 })
112 .filter(|t| !t.sources.is_empty())
113 .collect())
114}