foundry_cli/opts/build/
utils.rs1use eyre::Result;
2use foundry_compilers::{
3 CompilerInput, Graph, Project, ProjectCompileOutput, ProjectPathsConfig,
4 artifacts::{Source, Sources},
5 multi::{MultiCompilerLanguage, MultiCompilerParser},
6 solc::{SOLC_EXTENSIONS, SolcLanguage, SolcVersionedInput},
7};
8use foundry_config::{Config, semver::Version};
9use rayon::prelude::*;
10use solar::sema::ParsingContext;
11use std::{
12 collections::{HashSet, VecDeque},
13 path::{Path, PathBuf},
14};
15
16pub fn configure_pcx(
24 pcx: &mut ParsingContext<'_>,
25 config: &Config,
26 project: Option<&Project>,
27 target_paths: Option<&[PathBuf]>,
28) -> Result<()> {
29 let project = match project {
31 Some(project) => project,
32 None => &config.ephemeral_project()?,
33 };
34
35 let sources = match target_paths {
36 Some(targets) => {
38 let mut sources = Sources::new();
39 for t in targets {
40 let path = dunce::canonicalize(t)?;
41 let source = Source::read(&path)?;
42 sources.insert(path, source);
43 }
44 sources
45 }
46 None => project.paths.read_input_files()?,
48 };
49
50 let graph = Graph::<MultiCompilerParser>::resolve_sources(&project.paths, sources)?;
52 let (version, sources, _) = graph
53 .into_sources_by_version(project)?
55 .sources
56 .into_iter()
57 .find(|(lang, _)| *lang == MultiCompilerLanguage::Solc(SolcLanguage::Solidity))
59 .ok_or_else(|| eyre::eyre!("no Solidity sources"))?
60 .1
61 .into_iter()
62 .max_by(|(v1, _, _), (v2, _, _)| v1.cmp(v2))
64 .unwrap();
65
66 let solc = SolcVersionedInput::build(
67 sources,
68 config.solc_settings()?,
69 SolcLanguage::Solidity,
70 version,
71 );
72
73 configure_pcx_from_solc(pcx, &project.paths, &solc, true);
74
75 Ok(())
76}
77
78pub fn configure_pcx_from_compile_output(
85 pcx: &mut ParsingContext<'_>,
86 config: &Config,
87 output: &ProjectCompileOutput,
88 target_paths: Option<&[PathBuf]>,
89) -> Result<()> {
90 let is_solidity_file = |path: &Path| -> bool {
91 path.extension().and_then(|s| s.to_str()).is_some_and(|ext| SOLC_EXTENSIONS.contains(&ext))
92 };
93
94 let mut source_paths: HashSet<PathBuf> = if let Some(targets) = target_paths
96 && !targets.is_empty()
97 {
98 let mut source_paths = HashSet::new();
99 let mut queue: VecDeque<PathBuf> = targets
100 .iter()
101 .filter_map(|path| {
102 is_solidity_file(path).then(|| dunce::canonicalize(path).ok()).flatten()
103 })
104 .collect();
105
106 while let Some(path) = queue.pop_front() {
107 if source_paths.insert(path.clone()) {
108 for import in output.graph().imports(path.as_path()) {
109 queue.push_back(import.to_path_buf());
110 }
111 }
112 }
113
114 source_paths
115 } else {
116 output
117 .graph()
118 .source_files()
119 .filter_map(|idx| {
120 let path = output.graph().node_path(idx).to_path_buf();
121 is_solidity_file(&path).then_some(path)
122 })
123 .collect()
124 };
125
126 let (version, sources) = {
128 let (mut max_version, mut sources) = (Version::new(0, 0, 0), Sources::new());
129 for (id, _) in output.artifact_ids() {
130 if let Ok(path) = dunce::canonicalize(&id.source)
131 && source_paths.remove(&path)
132 {
133 if id.version > max_version {
134 max_version = id.version;
135 };
136
137 let source = Source::read(&path)?;
138 sources.insert(path, source);
139 }
140 }
141
142 (max_version, sources)
143 };
144
145 let solc = SolcVersionedInput::build(
146 sources,
147 config.solc_settings()?,
148 SolcLanguage::Solidity,
149 version,
150 );
151
152 configure_pcx_from_solc(pcx, &config.project_paths(), &solc, true);
153 Ok(())
154}
155
156pub fn configure_pcx_from_solc(
161 pcx: &mut ParsingContext<'_>,
162 project_paths: &ProjectPathsConfig,
163 vinput: &SolcVersionedInput,
164 add_source_files: bool,
165) {
166 configure_pcx_from_solc_cli(pcx, project_paths, &vinput.cli_settings);
167 if add_source_files {
168 let sources = vinput
169 .input
170 .sources
171 .par_iter()
172 .filter_map(|(path, source)| {
173 pcx.sess.source_map().new_source_file(path.clone(), source.content.as_str()).ok()
174 })
175 .collect::<Vec<_>>();
176 pcx.add_files(sources);
177 }
178}
179
180fn configure_pcx_from_solc_cli(
181 pcx: &mut ParsingContext<'_>,
182 project_paths: &ProjectPathsConfig,
183 cli_settings: &foundry_compilers::solc::CliSettings,
184) {
185 pcx.file_resolver
186 .set_current_dir(cli_settings.base_path.as_ref().unwrap_or(&project_paths.root));
187 for remapping in &project_paths.remappings {
188 pcx.file_resolver.add_import_remapping(solar::sema::interface::config::ImportRemapping {
189 context: remapping.context.clone().unwrap_or_default(),
190 prefix: remapping.name.clone(),
191 path: remapping.path.clone(),
192 });
193 }
194 pcx.file_resolver.add_include_paths(cli_settings.include_paths.iter().cloned());
195}