foundry_cli/opts/build/
utils.rs

1use crate::{opts::BuildOpts, utils::LoadConfig};
2
3use eyre::Result;
4use foundry_compilers::{
5    artifacts::{Source, Sources},
6    multi::{MultiCompilerLanguage, MultiCompilerParsedSource},
7    solc::{SolcLanguage, SolcVersionedInput},
8    CompilerInput, Graph, Project,
9};
10use solar_sema::{interface::Session, ParsingContext};
11use std::path::PathBuf;
12
13/// Builds a Solar [`solar_sema::ParsingContext`] from [`BuildOpts`].
14///
15/// * Configures include paths, remappings and registers all in-memory sources so that solar can
16///   operate without touching disk.
17/// * If no `target_paths` are provided, all project files are processed.
18/// * Only processes the subset of sources with the most up-to-date Solitidy version.
19pub fn solar_pcx_from_build_opts<'sess>(
20    sess: &'sess Session,
21    build: BuildOpts,
22    target_paths: Option<Vec<PathBuf>>,
23) -> Result<ParsingContext<'sess>> {
24    // Process build options
25    let config = build.load_config()?;
26    let project = config.ephemeral_project()?;
27
28    let sources = match target_paths {
29        // If target files are provided, only process those sources
30        Some(targets) => {
31            let mut sources = Sources::new();
32            for t in targets.into_iter() {
33                let path = dunce::canonicalize(t)?;
34                let source = Source::read(&path)?;
35                sources.insert(path, source);
36            }
37            sources
38        }
39        // Otherwise, process all project files
40        None => project.paths.read_input_files()?,
41    };
42
43    // Only process sources with latest Solidity version to avoid conflicts.
44    let graph = Graph::<MultiCompilerParsedSource>::resolve_sources(&project.paths, sources)?;
45    let (version, sources, _) = graph
46        // resolve graph into mapping language -> version -> sources
47        .into_sources_by_version(&project)?
48        .sources
49        .into_iter()
50        // only interested in Solidity sources
51        .find(|(lang, _)| *lang == MultiCompilerLanguage::Solc(SolcLanguage::Solidity))
52        .ok_or_else(|| eyre::eyre!("no Solidity sources"))?
53        .1
54        .into_iter()
55        // always pick the latest version
56        .max_by(|(v1, _, _), (v2, _, _)| v1.cmp(v2))
57        .unwrap();
58
59    let solc = SolcVersionedInput::build(
60        sources,
61        config.solc_settings()?,
62        SolcLanguage::Solidity,
63        version,
64    );
65
66    Ok(solar_pcx_from_solc_project(sess, &project, &solc, true))
67}
68
69/// Builds a Solar [`solar_sema::ParsingContext`] from a  [`foundry_compilers::Project`] and a
70/// [`SolcVersionedInput`].
71///
72/// * Configures include paths, remappings.
73/// * Source files can be manually added if the param `add_source_file` is set to `false`.
74pub fn solar_pcx_from_solc_project<'sess>(
75    sess: &'sess Session,
76    project: &Project,
77    solc: &SolcVersionedInput,
78    add_source_files: bool,
79) -> ParsingContext<'sess> {
80    // Configure the parsing context with the paths, remappings and sources
81    let mut pcx = ParsingContext::new(sess);
82
83    pcx.file_resolver
84        .set_current_dir(solc.cli_settings.base_path.as_ref().unwrap_or(&project.paths.root));
85    for remapping in &project.paths.remappings {
86        pcx.file_resolver.add_import_remapping(solar_sema::interface::config::ImportRemapping {
87            context: remapping.context.clone().unwrap_or_default(),
88            prefix: remapping.name.clone(),
89            path: remapping.path.clone(),
90        });
91    }
92    pcx.file_resolver.add_include_paths(solc.cli_settings.include_paths.iter().cloned());
93
94    if add_source_files {
95        for (path, source) in &solc.input.sources {
96            if let Ok(src_file) =
97                sess.source_map().new_source_file(path.clone(), source.content.as_str())
98            {
99                pcx.add_file(src_file);
100            }
101        }
102    }
103
104    pcx
105}