nom_kconfig/entry/source/
mod.rs1#[cfg(feature = "kconfiglib")]
2mod orsource;
3#[cfg(feature = "kconfiglib")]
4mod osource;
5#[cfg(feature = "kconfiglib")]
6mod rsource;
7#[allow(clippy::module_inception)]
8mod source;
9
10use nom::{
11 branch::alt,
12 character::complete::{alphanumeric1, one_of},
13 combinator::{cut, map, recognize},
14 error::{Error, ErrorKind, ParseError},
15 multi::many1,
16 IResult, Parser,
17};
18
19#[cfg(feature = "kconfiglib")]
20pub use self::{
21 orsource::parse_orsource, orsource::OrSource, osource::parse_osource, osource::OSource,
22 rsource::parse_rsource, rsource::RSource,
23};
24pub use source::{parse_source, Source};
25#[cfg(feature = "debug")]
26use tracing::{debug, error};
27
28use crate::{kconfig::private_parse_kconfig, KconfigInput};
29use crate::{util::ws, Kconfig, KconfigFile};
30
31#[cfg(feature = "glob-wildcard")]
32pub use glob::glob;
33use std::collections::HashMap;
34#[cfg(feature = "glob-wildcard")]
35use std::path::PathBuf;
36
37#[cfg(test)]
38mod source_test;
39
40#[cfg(feature = "glob-wildcard")]
41enum JoinPathMode {
42 Relative,
43
44 Root,
45}
46
47pub(crate) fn parse_filepath(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, &str> {
48 map(
49 recognize(ws(many1(alt((
50 alphanumeric1::<KconfigInput, _>,
51 recognize(one_of(".$(){}*-_$+@/")),
52 ))))),
53 |d| d.fragment().to_owned(),
54 )
55 .parse(input)
56}
57
58#[allow(clippy::type_complexity)]
59fn parse_source_kconfig(
60 input: KconfigInput,
61 source_kconfig_file: KconfigFile,
62) -> Result<(HashMap<String, String>, Kconfig), nom::Err<Error<KconfigInput>>> {
63 let source_content = source_kconfig_file
64 .read_to_string()
65 .map_err(|_| nom::Err::Error(Error::from_error_kind(input.clone(), ErrorKind::Fail)));
66
67 #[cfg(feature = "kconfiglib")]
68 {
69 if source_content.is_err() {
75 #[cfg(feature = "debug")]
76 error!(
77 "I tried to parse the source file '{}' defined in '{}'. This is likely because the filename is dynamically generated with macros/variables that are not supported yet. Returning an empty Kconfig for this source file.",
78 source_kconfig_file.full_path().display(),
79 input.extra.full_path().display()
80 );
81 return Ok((
82 input.extra.vars(),
83 Kconfig {
84 file: source_kconfig_file.full_path().display().to_string(),
85 entries: vec![],
86 },
87 ));
88 }
89 }
90
91 let source_content = source_content?;
92
93 #[allow(clippy::let_and_return)]
94 let x = match cut(private_parse_kconfig).parse(KconfigInput::new_extra(
95 &source_content,
96 source_kconfig_file.clone(),
97 )) {
98 Ok((d, kconfig)) => Ok(((*d.extra.local_vars).clone(), kconfig)),
99 Err(e) => {
100 match e {
106 nom::Err::Incomplete(needed) => {
107 return Err(nom::Err::Incomplete(needed));
108 }
109 nom::Err::Error(e) => {
110 return Err(nom::Err::Error(Error::new(
111 unsafe {
112 KconfigInput::new_from_raw_offset(
113 e.input.location_offset(),
114 e.input.location_line(),
115 String::leak(e.input.fragment().to_string()),
116 e.input.extra,
117 )
118 },
119 e.code,
120 )))
121 }
122 nom::Err::Failure(e) => {
123 return Err(nom::Err::Failure(Error::new(
124 unsafe {
125 KconfigInput::new_from_raw_offset(
126 e.input.location_offset(),
127 e.input.location_line(),
128 String::leak(e.input.fragment().to_string()),
129 e.input.extra,
130 )
131 },
132 e.code,
133 )));
134 }
135 }
136 }
137 };
138 x
139}
140
141#[cfg(feature = "glob-wildcard")]
142fn expand_source_files<'a>(
143 input: KconfigInput<'a>,
144 file: &str,
145 mode: JoinPathMode,
146) -> Result<Vec<PathBuf>, nom::Err<Error<KconfigInput<'a>>>> {
147 let mut expanded_files = Vec::new();
148 let prefix_path = match mode {
149 JoinPathMode::Relative => input.extra.full_path().parent().unwrap().to_path_buf(),
150 JoinPathMode::Root => input.extra.root_dir.clone(),
151 };
152
153 let full_path_pattern = prefix_path.join(file);
154 let paths: Vec<PathBuf> = glob(&full_path_pattern.display().to_string())
155 .map_err(|_| nom::Err::Error(Error::from_error_kind(input.clone(), ErrorKind::Fail)))?
156 .collect::<Result<Vec<_>, _>>()
157 .map_err(|_| nom::Err::Error(Error::from_error_kind(input.clone(), ErrorKind::Fail)))?;
158
159 if paths.is_empty() {
160 return Ok(vec![prefix_path.join(file)]);
161 }
162 for source_path in paths {
163 let source_path = source_path.canonicalize().unwrap();
164 let source_path_without_root = source_path
165 .strip_prefix(input.extra.root_dir.canonicalize().unwrap())
169 .map_err(|_| nom::Err::Error(Error::from_error_kind(input.clone(), ErrorKind::Fail)))?;
170 expanded_files.push(source_path_without_root.to_path_buf());
171 }
172
173 expanded_files.sort();
174 if expanded_files.is_empty() {
175 #[cfg(feature = "debug")]
176 debug!("No expanded files found");
177 return Err(nom::Err::Error(Error::from_error_kind(
178 input,
179 ErrorKind::Fail,
180 )));
181 }
182
183 Ok(expanded_files)
184}
185
186#[cfg(test)]
187use crate::assert_parsing_eq;
188
189#[test]
190fn test_parse_filepath() {
191 assert_parsing_eq!(
192 parse_filepath,
193 "u-boot/board/sagem/f@st1704/Kconfig",
194 Ok(("", "u-boot/board/sagem/f@st1704/Kconfig"))
195 );
196
197 assert_parsing_eq!(
198 parse_filepath,
199 "u-boot/board/l+g/vinco/Kconfig",
200 Ok(("", "u-boot/board/l+g/vinco/Kconfig"))
201 );
202}