Skip to main content

miden_assembly/project/
providers.rs

1mod masm;
2
3use miden_assembly_syntax::debuginfo::SourceManager;
4use miden_project::ProjectDependencyGraph;
5
6pub use self::masm::MasmSourceProvider;
7use super::*;
8
9/// This struct provides important context about the current target being assembled to
10/// implementations of the [ProjectSourceProvider] trait.
11pub struct TargetAssemblyContext<'a> {
12    /// The package manifest for the target being assembled
13    pub package: &'a ProjectPackage,
14    /// The resolved/canonicalized package manifest path
15    pub manifest_path: &'a std::path::Path,
16    /// The resolved/canonicalized path to the directory containing `manifest_path`
17    pub project_root: &'a std::path::Path,
18    /// The resolved/canonicalized path to the root source file of `target`
19    pub resolved_target_root: Box<std::path::Path>,
20    /// The target being assembled
21    pub target: &'a Target,
22    /// The build profile selected for this assembly session
23    pub profile: &'a Profile,
24    /// The dependency graph computed for this assembly session
25    pub dependency_graph: &'a ProjectDependencyGraph,
26    /// The current source manager
27    pub source_manager: Arc<dyn SourceManager>,
28    /// The assembler-wide `warnings_as_errors` flag
29    pub warnings_as_errors: bool,
30}
31
32impl<'a> TargetAssemblyContext<'a> {
33    pub fn new(
34        package: &'a ProjectPackage,
35        manifest_path: &'a std::path::Path,
36        target: &'a Target,
37        profile: &'a Profile,
38        dependency_graph: &'a ProjectDependencyGraph,
39        source_manager: Arc<dyn SourceManager>,
40    ) -> Result<Self, Report> {
41        let project_root = manifest_path.parent().ok_or_else(|| {
42            Report::msg(format!("manifest '{}' has no parent directory", manifest_path.display()))
43        })?;
44        let target_path = target.path.to_path().ok_or_else(|| {
45            Report::msg(format!(
46                "invalid target '{}': '{}' is not a valid file path",
47                target.name.inner(),
48                target.path
49            ))
50        })?;
51        let root_path = project_root.join(&target_path);
52        let root_path = root_path.canonicalize().map_err(|error| {
53            Report::msg(format!(
54                "failed to resolve target source '{}': {error}",
55                root_path.display()
56            ))
57        })?;
58        Ok(TargetAssemblyContext {
59            package,
60            manifest_path,
61            project_root,
62            resolved_target_root: root_path.into_boxed_path(),
63            target,
64            profile,
65            dependency_graph,
66            source_manager,
67            warnings_as_errors: false,
68        })
69    }
70
71    #[inline]
72    pub fn with_warnings_as_errors(&mut self, yes: bool) -> &mut Self {
73        self.warnings_as_errors = yes;
74        self
75    }
76}
77
78/// This trait provides source file inputs for a Miden Assembly project, regardless of the source
79/// language it was derived from.
80///
81/// For Miden Assembly source projects this is straightforward, see [MasmSourceProvider].
82///
83/// For languages other than MASM, which require a compilation step to produce Miden Assembly AST
84/// from the source language prior to assembly, this trait provides the necessary hooks so that
85/// the project assembler can request compilation of a project in source form on-demand.
86/// Implementors are given all available information needed to compile to MASM, and are expected
87/// to return requested artifacts to the project assembler.
88///
89/// Source providers are registered by the file type (i.e. file extension used by the source file)
90/// with the assembler when it is created. Only one source provider per-file-type is allowed.
91pub trait ProjectSourceProvider {
92    /// Returns the file extension this provider should be registered as handling, e.g. `rs`
93    fn file_type(&self) -> &'static str;
94    /// Called to request the compiled/parsed Miden Assembly AST corresponding to the current target
95    /// being assembled.
96    fn provide_sources(
97        &self,
98        context: &TargetAssemblyContext<'_>,
99    ) -> Result<ProjectSourceInputs, Report>;
100    /// Called to request the source files that are inputs to assembly of the current target, so
101    /// that source provenance hash for the target can be computed.
102    ///
103    /// It is expected that all source files that contribute to the build be included in the set
104    /// of source inputs returned, otherwise package identity for the assembled target will be
105    /// incomplete, and another instance of the same package may be used from the cache if the
106    /// source provenance appears unchanged, even when the artifacts produced would be different.
107    ///
108    /// For MASM packages, the above is already guaranteed - but for compilation of packages in
109    /// other languages, such as Rust, the toolchain invoking the assembler must ensure that all
110    /// build inputs are accounted for. Note that you _do not_ need to include the sources of
111    /// your Miden dependencies, and non-Miden dependencies can be accounted for by hashing a
112    /// dependency lock file if present (e.g. `Cargo.toml`).
113    fn provide_source_provenance(
114        &self,
115        context: &TargetAssemblyContext<'_>,
116    ) -> Result<ProjectSourceProvenanceInputs, Report>;
117}