normalize_languages/
c_cpp.rs1use crate::external_packages::ResolvedPackage;
4use std::path::PathBuf;
5use std::process::Command;
6
7pub fn get_gcc_version() -> Option<String> {
9 let output = Command::new("gcc").args(["--version"]).output().ok()?;
10
11 if output.status.success() {
12 let version_str = String::from_utf8_lossy(&output.stdout);
13 for line in version_str.lines() {
15 for part in line.split_whitespace() {
17 if part.chars().next().is_some_and(|c| c.is_ascii_digit()) {
18 let ver_parts: Vec<&str> = part.split('.').collect();
19 if ver_parts.len() >= 2 {
20 return Some(format!("{}.{}", ver_parts[0], ver_parts[1]));
21 }
22 }
23 }
24 }
25 }
26
27 let output = Command::new("clang").args(["--version"]).output().ok()?;
29 if output.status.success() {
30 let version_str = String::from_utf8_lossy(&output.stdout);
31 for line in version_str.lines() {
32 if line.contains("clang version") {
33 for part in line.split_whitespace() {
34 if part.chars().next().is_some_and(|c| c.is_ascii_digit()) {
35 let ver_parts: Vec<&str> = part.split('.').collect();
36 if ver_parts.len() >= 2 {
37 return Some(format!("{}.{}", ver_parts[0], ver_parts[1]));
38 }
39 }
40 }
41 }
42 }
43 }
44
45 None
46}
47
48pub fn find_cpp_include_paths() -> Vec<PathBuf> {
50 let mut paths = Vec::new();
51
52 let system_paths = [
54 "/usr/include",
55 "/usr/local/include",
56 "/usr/include/c++",
57 "/usr/include/x86_64-linux-gnu",
58 "/usr/include/aarch64-linux-gnu",
59 ];
60
61 for path in system_paths {
62 let p = PathBuf::from(path);
63 if p.is_dir() {
64 paths.push(p);
65 }
66 }
67
68 if let Ok(output) = Command::new("gcc")
70 .args(["-E", "-Wp,-v", "-xc", "/dev/null"])
71 .output()
72 {
73 let stderr = String::from_utf8_lossy(&output.stderr);
74 let mut in_search_list = false;
75
76 for line in stderr.lines() {
77 if line.contains("#include <...> search starts here:") {
78 in_search_list = true;
79 continue;
80 }
81 if line.contains("End of search list.") {
82 break;
83 }
84 if in_search_list {
85 let path = PathBuf::from(line.trim());
86 if path.is_dir() && !paths.contains(&path) {
87 paths.push(path);
88 }
89 }
90 }
91 }
92
93 if let Ok(output) = Command::new("clang")
95 .args(["-E", "-Wp,-v", "-xc", "/dev/null"])
96 .output()
97 {
98 let stderr = String::from_utf8_lossy(&output.stderr);
99 let mut in_search_list = false;
100
101 for line in stderr.lines() {
102 if line.contains("#include <...> search starts here:") {
103 in_search_list = true;
104 continue;
105 }
106 if line.contains("End of search list.") {
107 break;
108 }
109 if in_search_list {
110 let path = PathBuf::from(line.trim());
111 if path.is_dir() && !paths.contains(&path) {
112 paths.push(path);
113 }
114 }
115 }
116 }
117
118 #[cfg(target_os = "macos")]
120 {
121 let xcode_paths = [
123 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include",
124 "/Library/Developer/CommandLineTools/usr/include",
125 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include",
126 ];
127 for path in xcode_paths {
128 let p = PathBuf::from(path);
129 if p.is_dir() && !paths.contains(&p) {
130 paths.push(p);
131 }
132 }
133
134 let homebrew_paths = ["/opt/homebrew/include", "/usr/local/include"];
136 for path in homebrew_paths {
137 let p = PathBuf::from(path);
138 if p.is_dir() && !paths.contains(&p) {
139 paths.push(p);
140 }
141 }
142 }
143
144 paths
145}
146
147pub fn resolve_cpp_include(include: &str, include_paths: &[PathBuf]) -> Option<ResolvedPackage> {
149 let header = include
151 .trim_start_matches('<')
152 .trim_end_matches('>')
153 .trim_start_matches('"')
154 .trim_end_matches('"');
155
156 for base_path in include_paths {
158 let full_path = base_path.join(header);
159 if full_path.is_file() {
160 return Some(ResolvedPackage {
161 path: full_path,
162 name: header.to_string(),
163 is_namespace: false,
164 });
165 }
166
167 if !header.contains('.') {
169 for ext in &["", ".h", ".hpp", ".hxx"] {
171 let with_ext = if ext.is_empty() {
172 base_path.join(header)
173 } else {
174 base_path.join(format!("{}{}", header, ext))
175 };
176 if with_ext.is_file() {
177 return Some(ResolvedPackage {
178 path: with_ext,
179 name: header.to_string(),
180 is_namespace: false,
181 });
182 }
183 }
184 }
185 }
186
187 None
188}