foundry_cli/opts/build/
utils.rs

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