mz_deploy/cli/commands/
compile.rs1use crate::cli::CliError;
23use crate::cli::progress;
24use crate::config::Settings;
25use crate::project::ir::graph::Project;
26use crate::{project, verbose};
27use std::time::Instant;
28
29pub async fn run(settings: &Settings, show_progress: bool) -> Result<Project, CliError> {
51 run_with_fs(settings, show_progress, crate::fs::FileSystem::new()).await
52}
53
54pub(crate) async fn run_with_fs(
58 settings: &Settings,
59 show_progress: bool,
60 fs: crate::fs::FileSystem,
61) -> Result<Project, CliError> {
62 let settings = settings.clone();
63 mz_ore::task::spawn_blocking(
64 || "compile-run",
65 move || run_inner(&settings, show_progress, false, fs),
66 )
67 .await
68}
69
70pub async fn run_without_typecheck(
77 settings: &Settings,
78 show_progress: bool,
79) -> Result<Project, CliError> {
80 let settings = settings.clone();
81 mz_ore::task::spawn_blocking(
82 || "compile-run",
83 move || run_inner(&settings, show_progress, true, crate::fs::FileSystem::new()),
84 )
85 .await
86}
87
88fn run_inner(
89 settings: &Settings,
90 show_progress: bool,
91 skip_typecheck: bool,
92 fs: crate::fs::FileSystem,
93) -> Result<Project, CliError> {
94 let start_time = Instant::now();
95 let directory = &settings.directory;
96
97 if show_progress {
98 let canonical = directory.canonicalize();
99 let shown = canonical.as_deref().unwrap_or(directory);
100 progress::action("Compiling", &shown.display().to_string());
101 }
102
103 let planned_project = project::plan_sync(
104 &fs,
105 directory.clone(),
106 settings.profile_name(),
107 settings.profile_suffix(),
108 settings.variables(),
109 )?;
110
111 let validation = project::analysis::deps::validate_dependencies(
112 &settings.dependencies,
113 &planned_project.external_dependencies,
114 );
115
116 if !validation.unused.is_empty() {
117 let mut unused: Vec<_> = validation.unused.iter().collect();
118 unused.sort();
119 for dep in unused {
120 progress::warn(&format!(
121 "unused dependency: \"{}\" is declared in project.toml but not referenced",
122 dep
123 ));
124 }
125 }
126
127 if !validation.undeclared.is_empty() {
128 let mut undeclared: Vec<_> = validation.undeclared.into_iter().collect();
129 undeclared.sort();
130 return Err(CliError::UndeclaredDependencies { undeclared });
131 }
132
133 if !skip_typecheck {
134 typecheck_project(settings, &planned_project)?;
135 }
136
137 if show_progress && crate::log::verbose_enabled() {
138 print_verbose_details(&planned_project);
139 }
140
141 if show_progress {
142 let total_duration = start_time.elapsed();
143 progress::finished("compile", total_duration);
144 }
145
146 Ok(planned_project)
147}
148
149fn typecheck_project(settings: &Settings, planned_project: &Project) -> Result<(), CliError> {
151 let directory = &settings.directory;
152 use crate::project::compiler::typecheck;
153
154 let external_types = crate::types::load_types_lock(directory).unwrap_or_default();
155
156 let (_, stats) = typecheck::run(
157 directory,
158 settings.profile_name().unwrap_or(""),
159 settings.profile_suffix(),
160 settings.variables(),
161 planned_project,
162 external_types,
163 )?;
164 crate::verbose!(
165 "typecheck: ran={} skipped={} schema_stable={} schema_changed={}",
166 stats.ran,
167 stats.skipped,
168 stats.schema_stable,
169 stats.schema_changed,
170 );
171
172 Ok(())
173}
174
175fn print_verbose_details(planned_project: &Project) {
177 print_external_dependencies(planned_project);
178 print_cluster_dependencies(planned_project);
179 print_dependency_graph(planned_project);
180}
181
182fn print_external_dependencies(planned_project: &Project) {
186 if planned_project.external_dependencies.is_empty() {
187 return;
188 }
189 verbose!("\nExternal Dependencies (not defined in this project):");
190 let mut external: Vec<_> = planned_project.external_dependencies.iter().collect();
191 external.sort();
192 for dep in external {
193 verbose!(" - {}", dep);
194 }
195}
196
197fn print_cluster_dependencies(planned_project: &Project) {
199 if planned_project.cluster_dependencies.is_empty() {
200 return;
201 }
202 verbose!("\nCluster Dependencies:");
203 let mut clusters: Vec<_> = planned_project.cluster_dependencies.iter().collect();
204 clusters.sort_by_key(|c| &c.name);
205 for cluster in clusters {
206 verbose!(" - {}", cluster.name);
207 }
208}
209
210fn print_dependency_graph(planned_project: &Project) {
215 verbose!("\nDependency Graph:");
216 for (object_id, deps) in &planned_project.dependency_graph {
217 if deps.is_empty() {
218 continue;
219 }
220 verbose!(" {} depends on:", object_id);
221 for dep in deps {
222 if planned_project.external_dependencies.contains(dep) {
223 verbose!(" - {} (external)", dep);
224 } else {
225 verbose!(" - {}", dep);
226 }
227 }
228 }
229}