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