Skip to main content

ros_cmake_parser/doc/
mod.rs

1mod cmake_parse;
2mod cmake_positional;
3pub mod command;
4mod command_scope;
5pub mod ros;
6mod token;
7
8use crate::CMakeListsTokens;
9
10pub use cmake_parse::CMakeParse;
11pub use cmake_positional::{CMakePositional, Keyword};
12use command::CommandParseError;
13
14pub use command::Command;
15pub use command_scope::{CommandScope, ToCommandScope};
16pub use ros::{AmentTargetDependencies, RosCommand};
17pub use token::{declarations_by_keywords, TextNodeDeclaration, Token, TokenDeclarations};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct RawCommand<'t> {
21    pub identifier: std::borrow::Cow<'t, [u8]>,
22    pub tokens: Vec<Token<'t>>,
23}
24
25pub struct Doc<'t> {
26    tokens: CMakeListsTokens<'t>,
27}
28
29impl<'t> Doc<'t> {
30    pub fn raw_commands<'a: 't>(&'a self) -> impl Iterator<Item = RawCommand<'t>> + 'a {
31        self.tokens.command_invocations().map(|ci| RawCommand {
32            identifier: ci.identifier(),
33            tokens: ci.to_text_nodes(),
34        })
35    }
36
37    pub fn ros_commands<'a: 't>(&'a self) -> impl Iterator<Item = RosCommand<'t>> + 'a {
38        self.raw_commands()
39            .filter_map(|raw| RosCommand::from_raw(&raw))
40    }
41
42    pub fn to_commands_iter<'a: 't>(
43        &'a self,
44    ) -> impl Iterator<Item = Result<Command<'t>, CommandParseError>> {
45        self.raw_commands()
46            .map(move |raw| (raw.identifier, raw.tokens))
47            .map(move |(identifier, tokens)| match &identifier[..] {
48                b"add_compile_definitions" => to_command(tokens, Command::AddCompileDefinitions),
49                b"add_compile_options" => to_command(tokens, Command::AddCompileOptions),
50                b"add_custom_command" => to_command(tokens, Command::AddCustomCommand),
51                b"add_custom_target" => to_command(tokens, Command::AddCustomTarget),
52                b"add_definitions" => to_command(tokens, Command::AddDefinitions),
53                b"add_dependencies" => to_command(tokens, Command::AddDependencies),
54                b"add_executable" => to_command(tokens, Command::AddExecutable),
55                b"add_library" => to_command(tokens, Command::AddLibrary),
56                b"add_link_options" => to_command(tokens, Command::AddLinkOptions),
57                b"add_subdirectory" => to_command(tokens, Command::AddSubdirectory),
58                b"add_test" => to_command(tokens, Command::AddTest),
59                b"aux_source_directory" => to_command(tokens, Command::AuxSourceDirectory),
60                b"build_command" => to_command(tokens, Command::BuildCommand),
61                b"create_test_sourcelist" => to_command(tokens, Command::CreateTestSourceList),
62                b"define_property" => to_command(tokens, Command::DefineProperty),
63                b"enable_language" => to_command(tokens, Command::EnableLanguage),
64                b"enable_testing" => Ok(Command::EnableTesting),
65                b"export" => to_command(tokens, Command::Export),
66                b"fltk_wrap_ui" => to_command(tokens, Command::FLTKWrapUI),
67                b"get_source_file_property" => to_command(tokens, Command::GetSourceFileProperty),
68                b"get_target_property" => to_command(tokens, Command::GetTargetProperty),
69                b"get_test_property" => to_command(tokens, Command::GetTestProperty),
70                b"include_directories" => to_command(tokens, Command::IncludeDirectories),
71                b"include_external_msproject" => {
72                    to_command(tokens, Command::IncludeExternalMSProject)
73                }
74                b"include_regular_expression" => {
75                    to_command(tokens, Command::IncludeRegularExpression)
76                }
77                b"install" => to_command(tokens, Command::Install),
78                b"link_directories" => to_command(tokens, Command::LinkDirectories),
79                b"link_libraries" => to_command(tokens, Command::LinkLibraries),
80                b"load_cache" => to_command(tokens, Command::LoadCache),
81                b"project" => to_command(tokens, Command::Project),
82                b"remove_definitions" => to_command(tokens, Command::RemoveDefinitions),
83                b"set_source_files_properties" => {
84                    to_command(tokens, Command::SetSourceFileProperties)
85                }
86                b"set_target_properties" => to_command(tokens, Command::SetTargetProperties),
87                b"set_tests_properties" => to_command(tokens, Command::SetTestsProperties),
88                b"source_group" => to_command(tokens, Command::SourceGroup),
89                b"target_compile_definitions" => {
90                    to_command(tokens, Command::TargetCompileDefinitions)
91                }
92                b"target_compile_features" => to_command(tokens, Command::TargetCompileFeatures),
93                b"target_compile_options" => to_command(tokens, Command::TargetCompileOptions),
94                b"target_include_directories" => {
95                    to_command(tokens, Command::TargetIncludeDirectories)
96                }
97                b"target_link_directories" => to_command(tokens, Command::TargetLinkDirectories),
98                b"target_link_libraries" => to_command(tokens, Command::TargetLinkLibraries),
99                b"target_link_options" => to_command(tokens, Command::TargetLinkOptions),
100                b"target_precompile_headers" => {
101                    to_command(tokens, Command::TargetPrecompileHeaders)
102                }
103                b"target_sources" => to_command(tokens, Command::TargetSources),
104                b"try_compile" => to_command(tokens, Command::TryCompile),
105                b"try_run" => to_command(tokens, Command::TryRun),
106                b"ctest_build" => to_command(tokens, Command::CTestBuild),
107                b"ctest_configure" => to_command(tokens, Command::CTestConfigure),
108                b"ctest_coverage" => to_command(tokens, Command::CTestCoverage),
109                b"ctest_empty_binary_directory" => {
110                    to_command(tokens, Command::CTestEmptyBinaryDirectory)
111                }
112                b"ctest_memcheck" => to_command(tokens, Command::CTestMemCheck),
113                b"ctest_read_custom_files" => to_command(tokens, Command::CTestReadCustomFiles),
114                b"ctest_run_script" => to_command(tokens, Command::CTestRunScript),
115                b"ctest_sleep" => to_command(tokens, Command::CTestSleep),
116                b"ctest_start" => to_command(tokens, Command::CTestStart),
117                b"ctest_submit" => to_command(tokens, Command::CTestSubmit),
118                b"ctest_test" => to_command(tokens, Command::CTestTest),
119                b"ctest_update" => to_command(tokens, Command::CTestUpdate),
120                b"ctest_upload" => to_command(tokens, Command::CTestUpload),
121                b"build_name" => to_command(tokens, Command::BuildName),
122                b"exec_program" => to_command(tokens, Command::ExecProgram),
123                b"export_library_dependencies" => {
124                    to_command(tokens, Command::ExportLibraryDependencies)
125                }
126                b"install_files" => to_command(tokens, Command::InstallFiles),
127                b"install_programs" => to_command(tokens, Command::InstallPrograms),
128                b"install_targets" => to_command(tokens, Command::InstallTargets),
129                b"load_command" => to_command(tokens, Command::LoadCommand),
130                b"make_directory" => to_command(tokens, Command::MakeDirectory),
131                b"output_required_files" => to_command(tokens, Command::OutputRequiredFiles),
132                b"qt_wrap_cpp" => to_command(tokens, Command::QtWrapCpp),
133                b"qt_wrap_ui" => to_command(tokens, Command::QtWrapUi),
134                b"remove" => to_command(tokens, Command::Remove),
135                b"subdir_depends" => to_command(tokens, Command::SubdirDepends),
136                b"subdirs" => to_command(tokens, Command::Subdirs),
137                b"use_mangled_mesa" => to_command(tokens, Command::UseMangledMesa),
138                b"utility_source" => to_command(tokens, Command::UtilitySource),
139                b"variable_requires" => to_command(tokens, Command::VariableRequires),
140                b"write_file" => to_command(tokens, Command::WriteFile),
141                b"block" => to_command(tokens, Command::Block),
142                b"break" => to_command(tokens, Command::Break),
143                b"cmake_host_system_information" => {
144                    to_command(tokens, Command::CMakeHostSystemInformation)
145                }
146                b"cmake_language" => to_command(tokens, Command::CMakeLanguage),
147                b"cmake_minimum_required" => to_command(tokens, Command::CMakeMinimumRequired),
148                b"cmake_parse_arguments" => to_command(tokens, Command::CMakeParseArguments),
149                b"cmake_path" => to_command(tokens, Command::CMakePath),
150                b"cmake_policy" => to_command(tokens, Command::CMakePolicy),
151                b"configure_file" => to_command(tokens, Command::ConfigureFile),
152                b"continue" => to_command(tokens, Command::Continue),
153                b"else" => to_command(tokens, Command::Else),
154                b"elseif" => to_command(tokens, Command::ElseIf),
155                b"endblock" => to_command(tokens, Command::EndBlock),
156                b"endforeach" => to_command(tokens, Command::EndForEach),
157                b"endfunction" => to_command(tokens, Command::EndFunction),
158                b"endif" => to_command(tokens, Command::EndIf),
159                b"endmacro" => to_command(tokens, Command::EndMacro),
160                b"endwhile" => to_command(tokens, Command::EndWhile),
161                b"execute_process" => to_command(tokens, Command::ExecuteProcess),
162                b"file" => to_command(tokens, Command::File),
163                b"find_file" => to_command(tokens, Command::FindFile),
164                b"find_library" => to_command(tokens, Command::FindLibrary),
165                b"find_package" => to_command(tokens, Command::FindPackage),
166                b"find_path" => to_command(tokens, Command::FindPath),
167                b"find_program" => to_command(tokens, Command::FindProgram),
168                b"foreach" => to_command(tokens, Command::ForEach),
169                b"function" => to_command(tokens, Command::Function),
170                b"get_cmake_property" => to_command(tokens, Command::GetCMakeProperty),
171                b"get_directory_property" => to_command(tokens, Command::GetDirectoryProperty),
172                b"get_filename_component" => to_command(tokens, Command::GetFilenameComponent),
173                b"get_property" => to_command(tokens, Command::GetProperty),
174                b"if" => to_command(tokens, Command::If),
175                b"include" => to_command(tokens, Command::Include),
176                b"include_guard" => to_command(tokens, Command::IncludeGuard),
177                b"list" => to_command(tokens, Command::List),
178                b"macro" => to_command(tokens, Command::Macro),
179                b"mark_as_advanced" => to_command(tokens, Command::MarkAsAdvanced),
180                b"math" => to_command(tokens, Command::Math),
181                b"message" => to_command(tokens, Command::Message),
182                b"option" => to_command(tokens, Command::Option),
183                b"return" => to_command(tokens, Command::Return),
184                b"separate_arguments" => to_command(tokens, Command::SeparateArguments),
185                b"set" => to_command(tokens, Command::Set),
186                b"set_directory_properties" => to_command(tokens, Command::SetDirectoryProperties),
187                b"set_property" => to_command(tokens, Command::SetProperty),
188                b"site_name" => to_command(tokens, Command::SiteName),
189                b"string" => to_command(tokens, Command::String),
190                b"unset" => to_command(tokens, Command::Unset),
191                b"variable_watch" => to_command(tokens, Command::VariableWatch),
192                b"while" => to_command(tokens, Command::While),
193                unknown => Err(CommandParseError::UnknownCommand(
194                    String::from_utf8_lossy(unknown).to_string(),
195                )),
196            })
197    }
198
199    pub fn commands<'a: 't>(&'a self) -> Result<Vec<Command<'t>>, CommandParseError> {
200        self.to_commands_iter().collect()
201    }
202}
203
204impl<'t> From<CMakeListsTokens<'t>> for Doc<'t> {
205    fn from(tokens: CMakeListsTokens<'t>) -> Self {
206        Self { tokens }
207    }
208}
209fn to_command<'t, C, F>(tokens: Vec<Token<'t>>, f: F) -> Result<Command<'t>, CommandParseError>
210where
211    C: CMakeParse<'t>,
212    F: Fn(Box<C>) -> Command<'t>,
213{
214    CMakeParse::complete(&tokens).map(f)
215}
216
217#[cfg(test)]
218mod tests {
219    use super::{Doc, RosCommand};
220    use crate::parse_cmakelists;
221
222    #[test]
223    fn raw_commands_expose_unknown_and_known_commands() {
224        let src = br#"
225find_package(rclcpp REQUIRED)
226ament_target_dependencies(my_node rclcpp std_msgs)
227ament_package()
228foo_custom_macro(bar baz)
229"#;
230
231        let parsed = parse_cmakelists(src).unwrap();
232        let doc = Doc::from(parsed);
233        let raw = doc.raw_commands().collect::<Vec<_>>();
234
235        assert_eq!(raw.len(), 4);
236        assert_eq!(raw[0].identifier.as_ref(), b"find_package");
237        assert_eq!(raw[1].identifier.as_ref(), b"ament_target_dependencies");
238        assert_eq!(raw[2].identifier.as_ref(), b"ament_package");
239        assert_eq!(raw[3].identifier.as_ref(), b"foo_custom_macro");
240        assert_eq!(
241            raw[1]
242                .tokens
243                .iter()
244                .map(ToString::to_string)
245                .collect::<Vec<_>>(),
246            vec!["my_node", "rclcpp", "std_msgs"]
247        );
248    }
249
250    #[test]
251    fn ros_commands_parse_key_ament_and_catkin_commands() {
252        let src = br#"
253ament_target_dependencies(my_node SYSTEM rclcpp PUBLIC std_msgs INTERFACE sensor_msgs)
254catkin_package()
255ament_package()
256foo_custom_macro(bar baz)
257"#;
258
259        let parsed = parse_cmakelists(src).unwrap();
260        let doc = Doc::from(parsed);
261        let ros = doc.ros_commands().collect::<Vec<_>>();
262
263        assert_eq!(ros.len(), 3);
264        match &ros[0] {
265            RosCommand::AmentTargetDependencies(dep) => {
266                assert_eq!(dep.target.to_string(), "my_node");
267                assert_eq!(
268                    dep.dependencies
269                        .iter()
270                        .map(ToString::to_string)
271                        .collect::<Vec<_>>(),
272                    vec!["rclcpp", "std_msgs", "sensor_msgs"]
273                );
274            }
275            other => panic!("unexpected first ros command: {other:?}"),
276        }
277        assert!(matches!(ros[1], RosCommand::CatkinPackage));
278        assert!(matches!(ros[2], RosCommand::AmentPackage));
279    }
280
281    #[test]
282    fn commands_still_error_on_unknown_command() {
283        let src = br#"
284find_package(rclcpp REQUIRED)
285ament_package()
286"#;
287
288        let parsed = parse_cmakelists(src).unwrap();
289        let doc = Doc::from(parsed);
290        let err = doc.commands().unwrap_err();
291        assert_eq!(err.to_string(), "unknown command: ament_package");
292    }
293}