1use std::{
2 collections::{HashMap, HashSet},
3 io,
4 sync::Arc,
5};
6
7use crate::{
8 build::{Build, BuildBehaviour, BuildError, RemotePackageSourceSpec, SrcRockSource},
9 config::Config,
10 lockfile::{
11 FlushLockfileError, LocalPackage, LocalPackageId, LockConstraint, Lockfile, OptState,
12 PinnedState, ReadOnly, ReadWrite,
13 },
14 lua_installation::{LuaInstallation, LuaInstallationError},
15 lua_rockspec::BuildBackendSpec,
16 lua_version::LuaVersionUnset,
17 luarocks::{
18 install_binary_rock::{BinaryRockInstall, InstallBinaryRockError},
19 luarocks_installation::{LuaRocksError, LuaRocksInstallError, LuaRocksInstallation},
20 },
21 operations::resolve::{
22 build_dependencies_to_install, PackageInstallData, Resolve, ResolveDependenciesError,
23 },
24 package::{PackageName, PackageNameList, PackageReq},
25 remote_package_db::{RemotePackageDB, RemotePackageDBError},
26 rockspec::Rockspec,
27 tree::{self, InstallTree, Tree, TreeError},
28 workspace::{Workspace, WorkspaceTreeError},
29};
30
31pub use crate::operations::install::spec::PackageInstallSpec;
32
33use super::{DownloadedRockspec, RemoteRockDownload};
34use bon::Builder;
35use bytes::Bytes;
36use futures::stream::FuturesUnordered;
37use futures::StreamExt;
38use itertools::Itertools;
39use miette::Diagnostic;
40use thiserror::Error;
41use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
42use tokio::task::{JoinError, JoinHandle};
43
44use tracing::Instrument;
45pub mod spec;
46
47#[derive(Builder)]
51#[builder(start_fn = new, finish_fn(name = _build, vis = ""))]
52pub struct Install<'a, T>
53where
54 T: InstallTree + Clone + Send + Sync,
55{
56 #[builder(start_fn)]
57 config: &'a Config,
58 #[builder(field)]
59 packages: Vec<PackageInstallSpec>,
60 #[builder(setters(name = "_tree", vis = ""))]
61 tree: T,
62 package_db: Option<RemotePackageDB>,
63}
64
65impl<'a, State> InstallBuilder<'a, Tree, State>
66where
67 State: install_builder::State,
68{
69 pub fn workspace(
70 self,
71 workspace: &'a Workspace,
72 ) -> Result<InstallBuilder<'a, Tree, install_builder::SetTree<State>>, WorkspaceTreeError>
73 where
74 State::Tree: install_builder::IsUnset,
75 {
76 let config = self.config;
77 Ok(self._tree(workspace.tree(config)?))
78 }
79}
80
81impl<'a, T, State> InstallBuilder<'a, T, State>
82where
83 State: install_builder::State,
84 T: InstallTree + Clone + Send + Sync,
85{
86 pub fn tree(self, tree: T) -> InstallBuilder<'a, T, install_builder::SetTree<State>>
87 where
88 State::Tree: install_builder::IsUnset,
89 {
90 self._tree(tree)
91 }
92
93 pub fn packages(self, packages: Vec<PackageInstallSpec>) -> Self {
94 Self { packages, ..self }
95 }
96
97 pub fn package(self, package: PackageInstallSpec) -> Self {
98 Self {
99 packages: self
100 .packages
101 .into_iter()
102 .chain(std::iter::once(package))
103 .collect(),
104 ..self
105 }
106 }
107}
108
109impl<State, T> InstallBuilder<'_, T, State>
110where
111 State: install_builder::State + install_builder::IsComplete,
112 T: InstallTree + Clone + Send + Sync + 'static,
113{
114 pub async fn install(self) -> Result<Vec<LocalPackage>, InstallError> {
116 let install_built = self._build();
117 if install_built.packages.is_empty() {
118 return Ok(Vec::default());
119 }
120 let count = install_built.packages.len();
121 let span = if count > 1 {
122 tracing::info_span!("Installing", count)
123 } else {
124 let install_spec = &install_built.packages[0];
125 tracing::info_span!("Installing", package = install_spec.package.to_string())
126 };
127 install_impl(install_built).instrument(span).await
128 }
129}
130
131type InstallWorkerOutput = Result<(LocalPackageId, (LocalPackage, tree::EntryType)), InstallError>;
132
133#[derive(Error, Debug, Diagnostic)]
134pub enum InstallError {
135 #[error("unable to resolve dependencies")]
136 #[diagnostic(forward(0))]
137 ResolveDependencies(#[from] Box<ResolveDependenciesError>),
138 #[error(transparent)]
139 #[diagnostic(transparent)]
140 LuaVersionUnset(#[from] LuaVersionUnset),
141 #[error(transparent)]
142 #[diagnostic(transparent)]
143 LuaInstallation(#[from] LuaInstallationError),
144 #[error(transparent)]
145 #[diagnostic(transparent)]
146 FlushLockfile(#[from] FlushLockfileError),
147 #[error(transparent)]
148 #[diagnostic(transparent)]
149 Tree(#[from] TreeError),
150 #[error(transparent)]
151 #[diagnostic(transparent)]
152 WorkspaceTree(#[from] WorkspaceTreeError),
153 #[error("error instantiating LuaRocks compatibility layer")]
154 #[diagnostic(forward(0))]
155 LuaRocks(#[from] LuaRocksError),
156 #[error("error installing LuaRocks compatibility layer")]
157 #[diagnostic(forward(0))]
158 LuaRocksInstall(#[from] Box<LuaRocksInstallError>),
159 #[error("failed to build {0}")]
160 Build(PackageName, #[source] Box<BuildError>),
161 #[error("failed to install build depencency {0}")]
162 BuildDependency(PackageName, #[source] Box<BuildError>),
163 #[error("error initialising remote package DB")]
164 #[diagnostic(forward(0))]
165 RemotePackageDB(#[from] RemotePackageDBError),
166 #[error("failed to install pre-built rock {0}")]
167 InstallBinaryRock(PackageName, #[source] Box<InstallBinaryRockError>),
168 #[error("cannot install duplicate entrypoints:\n{0}")]
169 DuplicateEntrypoints(PackageNameList),
170 #[error("install worker panicked")]
171 #[diagnostic(help(
172 r#"this is a bug in Lux, please report it, ideally with `RUST_BACKTRACE=1`.
173retrying with fewer parallel jobs (`--max-jobs`) may avoid the panic in the meantime"#
174 ))]
175 Join(#[from] tokio::task::JoinError),
176}
177
178impl From<ResolveDependenciesError> for InstallError {
179 fn from(source: ResolveDependenciesError) -> Self {
180 Self::ResolveDependencies(Box::new(source))
181 }
182}
183
184impl From<LuaRocksInstallError> for InstallError {
185 fn from(source: LuaRocksInstallError) -> Self {
186 Self::LuaRocksInstall(Box::new(source))
187 }
188}
189
190async fn install_impl<T>(install: Install<'_, T>) -> Result<Vec<LocalPackage>, InstallError>
191where
192 T: InstallTree + Clone + Send + Sync + 'static,
193{
194 let package_db = match install.package_db {
195 Some(db) => db,
196 None => RemotePackageDB::from_config(install.config).await?,
197 };
198
199 let duplicate_entrypoints = install
200 .packages
201 .iter()
202 .filter(|pkg| pkg.entry_type == tree::EntryType::Entrypoint)
203 .map(|pkg| pkg.package.name())
204 .duplicates()
205 .cloned()
206 .collect_vec();
207
208 if !duplicate_entrypoints.is_empty() {
209 return Err(InstallError::DuplicateEntrypoints(PackageNameList::new(
210 duplicate_entrypoints,
211 )));
212 }
213
214 let packages = install.packages;
215 let package_db = Arc::new(package_db);
216 let config = install.config;
217 let tree = &install.tree;
218
219 let (dep_tx, mut dep_rx) = tokio::sync::mpsc::unbounded_channel();
220 let (build_dep_tx, build_dep_rx) = tokio::sync::mpsc::unbounded_channel();
221 let (build_dep_install_done_tx, mut build_dep_install_done_rx) =
222 tokio::sync::mpsc::unbounded_channel::<(LocalPackage, PackageInstallData)>();
223
224 let lockfile = tree.lockfile()?;
225 let build_lockfile = tree.build_tree(config)?.lockfile()?;
226
227 let lua = Arc::new(LuaInstallation::new_from_config(config).await?);
228
229 let mut resolve_worker = spawn_resolve_worker(
230 config,
231 packages,
232 package_db,
233 lockfile.clone(),
234 build_lockfile.clone(),
235 dep_tx,
236 build_dep_tx,
237 );
238 let mut build_deps_worker = spawn_build_deps_worker(
239 config,
240 tree,
241 lua.clone(),
242 build_dep_rx,
243 build_dep_install_done_tx,
244 );
245
246 let mut all_packages: HashMap<LocalPackageId, PackageInstallData> = HashMap::new();
247 let mut scheduled_packages: HashSet<LocalPackageId> = HashSet::new();
248 let mut installed_packages: HashMap<LocalPackageId, (LocalPackage, tree::EntryType)> =
249 HashMap::new();
250 let mut installed_build_deps: HashMap<LocalPackageId, (LocalPackage, PackageInstallData)> =
251 HashMap::new();
252 let mut ongoing_installs: FuturesUnordered<
253 tracing::instrument::Instrumented<tokio::task::JoinHandle<InstallWorkerOutput>>,
254 > = FuturesUnordered::new();
255 let mut resolve_done = false;
256 let mut build_deps_done = false;
257 let mut dep_rx_drained = false;
258 let mut build_dep_rx_drained = false;
259 let mut install_loop_result: Result<(), InstallError> = Ok(());
260 let max_jobs = config.max_jobs();
261
262 'install: loop {
263 if resolve_done && build_deps_done && dep_rx_drained && build_dep_rx_drained {
264 break;
265 }
266 tokio::select! {
267 resolve_result = &mut resolve_worker, if !resolve_done => {
268 if let Err(err) = worker_result(resolve_result) {
269 install_loop_result = Err(*err);
270 break 'install;
271 }
272 resolve_done = true;
273 }
274 build_deps_result = &mut build_deps_worker, if !build_deps_done => {
275 if let Err(err) = worker_result(build_deps_result) {
276 install_loop_result = Err(*err);
277 break 'install;
278 }
279 build_deps_done = true;
280 }
281 build_dep = build_dep_install_done_rx.recv(), if !build_dep_rx_drained => {
282 if let Some((build_dep, install_data)) = build_dep {
283 installed_build_deps.insert(build_dep.spec.id(), (build_dep, install_data));
284 } else {
285 build_dep_rx_drained = true;
286 }
287 }
288 dep = dep_rx.recv(), if !dep_rx_drained => {
289 if let Some(dep) = dep {
290 all_packages.insert(dep.spec.id(), dep);
291 } else {
292 dep_rx_drained = true;
293 }
294 }
295 }
296
297 for (package_id, package_install_data) in ready_to_install(
298 &all_packages,
299 &scheduled_packages,
300 &build_lockfile,
301 &installed_build_deps
302 .values()
303 .map(|(pkg, _)| pkg.name().clone())
304 .collect(),
305 ) {
306 if max_jobs > 0 && ongoing_installs.len() >= max_jobs {
307 if let Err(err) =
308 wait_for_next_install(&mut ongoing_installs, &mut installed_packages).await
309 {
310 install_loop_result = Err(err);
311 break 'install;
312 }
313 }
314 scheduled_packages.insert(package_id);
315 ongoing_installs.push(spawn_install_worker(
316 package_install_data,
317 &lua,
318 tree,
319 config,
320 ));
321 }
322 }
323
324 match install_loop_result {
325 Ok(_) => {
326 while wait_for_next_install(&mut ongoing_installs, &mut installed_packages).await? {}
327
328 lockfile.map_then_flush(|lockfile| {
329 for (package_id, (package, is_entrypoint)) in installed_packages.iter().unique() {
330 lockfile.add_dependencies(
331 package_id,
332 package,
333 *is_entrypoint,
334 &all_packages,
335 &installed_packages,
336 )?;
337 lockfile.add_build_dependencies(
338 package_id,
339 package,
340 &all_packages,
341 &installed_build_deps,
342 )?;
343 }
344 Ok::<_, io::Error>(())
345 })?;
346
347 Ok(installed_packages
348 .into_values()
349 .map(|(pkg, _)| pkg)
350 .collect_vec())
351 }
352 Err(err) => {
353 resolve_worker.into_inner().abort();
354 build_deps_worker.into_inner().abort();
355 for install in ongoing_installs {
356 install.into_inner().abort();
357 }
358 Err(err)
359 }
360 }
361}
362
363fn spawn_resolve_worker(
364 config: &Config,
365 packages: Vec<PackageInstallSpec>,
366 package_db: Arc<RemotePackageDB>,
367 lockfile: Lockfile<ReadOnly>,
368 build_lockfile: Lockfile<ReadOnly>,
369 dep_tx: UnboundedSender<PackageInstallData>,
370 build_dep_tx: UnboundedSender<PackageInstallData>,
371) -> tracing::instrument::Instrumented<JoinHandle<Result<(), InstallError>>> {
372 tokio::spawn({
373 let config = config.clone();
374 let lockfile = Arc::new(lockfile);
375 let build_lockfile = Arc::new(build_lockfile);
376 async move {
377 Resolve::new()
378 .dependencies_tx(dep_tx)
379 .build_dependencies_tx(build_dep_tx)
380 .packages(packages)
381 .package_db(package_db)
382 .lockfile(lockfile)
383 .build_lockfile(build_lockfile)
384 .config(&config)
385 .get_all_dependencies()
386 .await?;
387 Ok::<(), InstallError>(())
388 }
389 })
390 .instrument(tracing::trace_span!("resolve_worker"))
391}
392
393fn spawn_build_deps_worker<T>(
394 config: &Config,
395 tree: &T,
396 lua: Arc<LuaInstallation>,
397 mut build_dep_rx: UnboundedReceiver<PackageInstallData>,
398 build_dep_install_done_tx: UnboundedSender<(LocalPackage, PackageInstallData)>,
399) -> tracing::instrument::Instrumented<JoinHandle<Result<(), InstallError>>>
400where
401 T: InstallTree + Clone + Send + Sync + 'static,
402{
403 tokio::spawn({
404 let config = config.clone();
405 let tree = tree.clone();
406 let lua = lua.clone();
407 async move {
408 while let Some(build_dep_spec) = build_dep_rx.recv().await {
409 let rockspec = build_dep_spec.downloaded_rock.rockspec();
410 let package = rockspec.package().clone();
411 let span = tracing::info_span!(
412 "Installing build dependency",
413 package = package.to_string(),
414 version = rockspec.version().to_string()
415 );
416 let pkg = async {
417 let build_tree = tree.build_tree(&config)?;
418 let mut build_lockfile = build_tree.lockfile()?.write_guard();
419 let pkg = Build::new()
420 .rockspec(rockspec)
421 .lua(&lua)
422 .tree(&build_tree)
423 .entry_type(tree::EntryType::Entrypoint)
424 .config(&config)
425 .constraint(build_dep_spec.spec.constraint())
426 .behaviour(build_dep_spec.build_behaviour)
427 .build()
428 .await
429 .map_err(|err| InstallError::BuildDependency(package, Box::new(err)))?;
430 build_lockfile.add_entrypoint(&pkg);
431 Ok::<_, InstallError>(pkg)
432 }
433 .instrument(span)
434 .await?;
435 let _ = build_dep_install_done_tx.send((pkg, build_dep_spec));
436 }
437 Ok::<(), InstallError>(())
438 }
439 })
440 .instrument(tracing::trace_span!("build_deps_worker"))
441}
442
443fn ready_to_install(
444 all_packages: &HashMap<LocalPackageId, PackageInstallData>,
445 scheduled: &HashSet<LocalPackageId>,
446 build_lockfile: &Lockfile<ReadOnly>,
447 installed_build_deps: &HashSet<PackageName>,
448) -> Vec<(LocalPackageId, PackageInstallData)> {
449 all_packages
450 .iter()
451 .filter(|(id, data)| {
452 !scheduled.contains(*id)
453 && match &data.downloaded_rock {
454 RemoteRockDownload::BinaryRock { .. } => true,
455 _ => build_dependencies_ready(
456 data.downloaded_rock.rockspec(),
457 data.build_behaviour,
458 build_lockfile,
459 installed_build_deps,
460 ),
461 }
462 })
463 .map(|(id, data)| (id.clone(), data.clone()))
464 .collect()
465}
466
467fn spawn_install_worker<T>(
468 data: PackageInstallData,
469 lua: &Arc<LuaInstallation>,
470 tree: &T,
471 config: &Config,
472) -> tracing::instrument::Instrumented<JoinHandle<InstallWorkerOutput>>
473where
474 T: InstallTree + Clone + Send + Sync + 'static,
475{
476 let config = config.clone();
477 let tree = tree.clone();
478 let lua = lua.clone();
479 let entry_type = data.entry_type;
480 tokio::spawn(async move {
481 let pkg = install_package(data, &lua, &tree, &config).await?;
482 Ok::<_, InstallError>((pkg.id(), (pkg, entry_type)))
483 })
484 .instrument(tracing::trace_span!("install_worker"))
485}
486
487#[tracing::instrument(level = "trace", skip_all)]
488async fn install_package<T>(
489 data: PackageInstallData,
490 lua: &Arc<LuaInstallation>,
491 tree: &T,
492 config: &Config,
493) -> Result<LocalPackage, InstallError>
494where
495 T: InstallTree + Sync,
496{
497 match data.downloaded_rock {
498 RemoteRockDownload::RockspecOnly { rockspec_download } => {
499 install_rockspec(
500 rockspec_download,
501 None,
502 data.spec.constraint(),
503 data.build_behaviour,
504 data.pin,
505 data.opt,
506 data.entry_type,
507 lua,
508 tree,
509 config,
510 )
511 .await
512 }
513 RemoteRockDownload::BinaryRock {
514 rockspec_download,
515 packed_rock,
516 } => {
517 install_binary_rock(
518 rockspec_download,
519 packed_rock,
520 data.spec.constraint(),
521 data.build_behaviour,
522 data.pin,
523 data.opt,
524 data.entry_type,
525 config,
526 tree,
527 )
528 .await
529 }
530 RemoteRockDownload::SrcRock {
531 rockspec_download,
532 src_rock,
533 source_url,
534 } => {
535 let src_rock_source = SrcRockSource {
536 bytes: src_rock,
537 source_url,
538 };
539 install_rockspec(
540 rockspec_download,
541 Some(src_rock_source),
542 data.spec.constraint(),
543 data.build_behaviour,
544 data.pin,
545 data.opt,
546 data.entry_type,
547 lua,
548 tree,
549 config,
550 )
551 .await
552 }
553 }
554}
555
556fn worker_result(
557 result: Result<Result<(), InstallError>, JoinError>,
558) -> Result<(), Box<InstallError>> {
559 match result {
560 Ok(Ok(())) => Ok(()),
561 Ok(Err(err)) => Err(err.into()),
562 Err(join) => Err(InstallError::from(join).into()),
563 }
564}
565
566async fn wait_for_next_install(
567 ongoing_installs: &mut FuturesUnordered<
568 tracing::instrument::Instrumented<tokio::task::JoinHandle<InstallWorkerOutput>>,
569 >,
570 installed_packages: &mut HashMap<LocalPackageId, (LocalPackage, tree::EntryType)>,
571) -> Result<bool, InstallError> {
572 if let Some(result) = ongoing_installs.next().await {
573 match result {
574 Ok(Ok((id, installed))) => {
575 installed_packages.insert(id, installed);
576 Ok(true)
577 }
578 Ok(Err(err)) => Err(err),
579 Err(join) => Err(InstallError::from(join)),
580 }
581 } else {
582 Ok(false)
583 }
584}
585trait LockfileExt {
586 fn add_dependencies(
587 self,
588 id: &LocalPackageId,
589 pkg: &LocalPackage,
590 entry_type: tree::EntryType,
591 all_packages: &HashMap<LocalPackageId, PackageInstallData>,
592 installed_packages: &HashMap<LocalPackageId, (LocalPackage, tree::EntryType)>,
593 ) -> io::Result<()>;
594
595 fn add_build_dependencies(
596 self,
597 id: &LocalPackageId,
598 pkg: &LocalPackage,
599 all_packages: &HashMap<LocalPackageId, PackageInstallData>,
600 build_dependencies: &HashMap<LocalPackageId, (LocalPackage, PackageInstallData)>,
601 ) -> io::Result<()>;
602}
603
604impl LockfileExt for &mut Lockfile<ReadWrite> {
605 fn add_dependencies(
606 self,
607 id: &LocalPackageId,
608 pkg: &LocalPackage,
609 entry_type: tree::EntryType,
610 all_packages: &HashMap<LocalPackageId, PackageInstallData>,
611 installed_packages: &HashMap<LocalPackageId, (LocalPackage, tree::EntryType)>,
612 ) -> io::Result<()> {
613 if entry_type == tree::EntryType::Entrypoint {
614 self.add_entrypoint(pkg);
615 }
616
617 for dependency_id in all_packages
618 .get(id)
619 .map(|pkg| pkg.spec.dependencies())
620 .unwrap_or_default()
621 .into_iter()
622 {
623 self.add_dependency(
624 pkg,
625 installed_packages
626 .get(dependency_id)
627 .map(|(pkg, _)| pkg)
628 .ok_or(io::Error::other(
629 r#"
630error writing dependencies to the lockfile.
631A required dependency was not installed correctly.
632This is likely because an install thread panicked and was interrupted unexpectedly.
633
634[THIS IS A BUG!]
635"#,
636 ))?,
637 );
638 }
639 Ok(())
640 }
641
642 fn add_build_dependencies(
643 self,
644 id: &LocalPackageId,
645 pkg: &LocalPackage,
646 all_packages: &HashMap<LocalPackageId, PackageInstallData>,
647 build_dependencies: &HashMap<LocalPackageId, (LocalPackage, PackageInstallData)>,
648 ) -> io::Result<()> {
649 for dependency_id in all_packages
650 .get(id)
651 .map(|pkg| pkg.spec.build_dependencies())
652 .unwrap_or_default()
653 .into_iter()
654 {
655 self.add_build_dependency(
656 pkg,
657 build_dependencies
658 .get(dependency_id)
659 .map(|(pkg, _)| pkg)
660 .ok_or(io::Error::other(
661 r#"
662error writing build dependencies to the lockfile.
663A required build dependency was not installed correctly.
664This is likely because an install thread panicked and was interrupted unexpectedly.
665
666[THIS IS A BUG!]
667"#,
668 ))?,
669 );
670 }
671 Ok(())
672 }
673}
674
675fn build_dependencies_ready(
682 rockspec: &impl Rockspec,
683 behaviour: BuildBehaviour,
684 build_lockfile: &Lockfile<ReadOnly>,
685 installed: &HashSet<PackageName>,
686) -> bool {
687 let build_deps = rockspec.build_dependencies().current_platform();
688 build_dependencies_to_install(rockspec).iter().all(|name| {
689 installed.contains(name)
690 || (behaviour != BuildBehaviour::Force
691 && build_lockfile
692 .has_rock(
693 &build_deps
694 .iter()
695 .find(|dep| dep.name() == name)
696 .map(|dep| dep.package_req().clone())
697 .unwrap_or_else(|| PackageReq::from(name.clone())),
698 None,
699 )
700 .is_some())
701 })
702}
703
704#[allow(clippy::too_many_arguments)]
705#[tracing::instrument(
706 name = "Installing",
707 level = "info",
708 skip_all,
709 fields(
710 package = rockspec_download.rockspec.package().to_string(),
711 version = rockspec_download.rockspec.version().to_string(),
712 ),
713)]
714async fn install_rockspec<T>(
715 rockspec_download: DownloadedRockspec,
716 src_rock_source: Option<SrcRockSource>,
717 constraint: LockConstraint,
718 behaviour: BuildBehaviour,
719 pin: PinnedState,
720 opt: OptState,
721 entry_type: tree::EntryType,
722 lua: &LuaInstallation,
723 tree: &T,
724 config: &Config,
725) -> Result<LocalPackage, InstallError>
726where
727 T: InstallTree + Sync,
728{
729 let package = rockspec_download.rockspec.package().clone();
730 let rockspec = rockspec_download.rockspec;
731 let source = rockspec_download.source;
732
733 if let Some(BuildBackendSpec::LuaRock(_)) = &rockspec.build().current_platform().build_backend {
734 let luarocks_tree = tree.build_tree(config)?;
735 let luarocks = LuaRocksInstallation::new(config, luarocks_tree)?;
736 luarocks.ensure_installed(lua).await?;
737 }
738
739 let source_spec = match src_rock_source {
740 Some(src_rock_source) => RemotePackageSourceSpec::SrcRock(src_rock_source),
741 None => RemotePackageSourceSpec::RockSpec(rockspec_download.source_url),
742 };
743
744 let pkg = Build::new()
745 .rockspec(&rockspec)
746 .lua(lua)
747 .tree(tree)
748 .entry_type(entry_type)
749 .config(config)
750 .pin(pin)
751 .opt(opt)
752 .constraint(constraint)
753 .behaviour(behaviour)
754 .source(source)
755 .source_spec(source_spec)
756 .build()
757 .await
758 .map_err(|err| InstallError::Build(package, Box::new(err)))?;
759 Ok(pkg)
760}
761
762#[allow(clippy::too_many_arguments)]
763#[tracing::instrument(
764 name = "Installing (pre-built)",
765 level = "info",
766 skip_all,
767 fields(
768 package = rockspec_download.rockspec.package().to_string(),
769 version = rockspec_download.rockspec.version().to_string(),
770 ),
771)]
772async fn install_binary_rock(
773 rockspec_download: DownloadedRockspec,
774 packed_rock: Bytes,
775 constraint: LockConstraint,
776 behaviour: BuildBehaviour,
777 pin: PinnedState,
778 opt: OptState,
779 entry_type: tree::EntryType,
780 config: &Config,
781 tree: &impl InstallTree,
782) -> Result<LocalPackage, InstallError> {
783 let rockspec = rockspec_download.rockspec;
784 let package = rockspec.package().clone();
785 let pkg = BinaryRockInstall::new(
786 &rockspec,
787 rockspec_download.source,
788 packed_rock,
789 entry_type,
790 config,
791 tree,
792 )
793 .pin(pin)
794 .opt(opt)
795 .constraint(constraint)
796 .behaviour(behaviour)
797 .install()
798 .await
799 .map_err(|err| InstallError::InstallBinaryRock(package, Box::new(err)))?;
800 Ok(pkg)
801}