Fork of daniellemaywood.uk/gleam — Wasm codegen work
5.1 kB
155 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2022 The Gleam contributors
3
4use camino::Utf8PathBuf;
5use gleam_core::{
6 Error, Result,
7 build::{Mode, Target, Telemetry},
8 error::{FileIoAction, FileKind},
9 paths::ProjectPaths,
10};
11use strum::IntoEnumIterator;
12
13#[derive(Debug)]
14pub(crate) struct BuildLock {
15 directory: Utf8PathBuf,
16 filename: String,
17}
18
19impl BuildLock {
20 /// Lock the build directory for the specified mode and target.
21 pub fn new_target(paths: &ProjectPaths, mode: Mode, target: Target) -> Result<Self> {
22 let directory = paths.build_directory();
23 crate::fs::mkdir(&directory)?;
24 let target = match target {
25 Target::Erlang => "erlang",
26 Target::JavaScript => "javascript",
27 };
28 Ok(Self {
29 directory,
30 filename: format!("gleam-{mode}-{target}.lock"),
31 })
32 }
33
34 /// Lock the packages directory.
35 pub fn new_packages(paths: &ProjectPaths) -> Result<Self> {
36 let directory = paths.build_packages_directory();
37 crate::fs::mkdir(&directory)?;
38 Ok(Self {
39 directory,
40 filename: "gleam.lock".to_string(),
41 })
42 }
43
44 /// Construct the lock file path
45 pub fn lock_path(&self) -> Utf8PathBuf {
46 self.directory.join(&self.filename)
47 }
48
49 /// Lock the directory specified by the lock
50 pub fn lock<Telem: Telemetry + ?Sized>(&self, telemetry: &Telem) -> Result<Guard> {
51 let lock_path = self.lock_path();
52 tracing::debug!(path=?lock_path, "locking_directory");
53
54 crate::fs::mkdir(&self.directory)?;
55
56 let mut file = fslock::LockFile::open(lock_path.as_str()).map_err(|e| Error::FileIo {
57 kind: FileKind::File,
58 path: lock_path.clone(),
59 action: FileIoAction::Create,
60 err: Some(e.to_string()),
61 })?;
62
63 let lock_error = |error: fslock::Error| Error::FileIo {
64 kind: FileKind::File,
65 action: FileIoAction::Lock,
66 path: lock_path.clone(),
67 err: Some(error.to_string()),
68 };
69
70 if !file.try_lock_with_pid().map_err(lock_error)? {
71 telemetry.waiting_for_build_directory_lock();
72 file.lock_with_pid().map_err(lock_error)?
73 }
74
75 Ok(Guard(file))
76 }
77
78 /// Lock all build directories. Does not lock the packages directory.
79 pub fn lock_all_build<Telem: Telemetry>(
80 paths: &ProjectPaths,
81 telemetry: &Telem,
82 ) -> Result<Vec<Guard>> {
83 let mut locks = vec![];
84 for mode in Mode::iter() {
85 for target in Target::iter() {
86 locks.push(Self::new_target(paths, mode, target)?.lock(telemetry)?);
87 }
88 }
89
90 Ok(locks)
91 }
92}
93
94#[derive(Debug)]
95pub(crate) struct Guard(
96 // False positive. This is used in `drop`. Presumably the lint error is a
97 // bug in clippy.
98 #[allow(dead_code)] fslock::LockFile,
99);
100
101#[test]
102fn locking_global() {
103 let paths = crate::project_paths_at_current_directory_without_toml();
104 let lock = BuildLock::new_packages(&paths).expect("make lock");
105 let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
106 println!("Locked!")
107}
108
109#[test]
110fn locking_dev_erlang() {
111 let paths = crate::project_paths_at_current_directory_without_toml();
112 let lock = BuildLock::new_target(&paths, Mode::Dev, Target::Erlang).expect("make lock");
113 let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
114 println!("Locked!")
115}
116
117#[test]
118fn locking_prod_erlang() {
119 let paths = crate::project_paths_at_current_directory_without_toml();
120 let lock = BuildLock::new_target(&paths, Mode::Prod, Target::Erlang).expect("make lock");
121 let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
122 println!("Locked!")
123}
124
125#[test]
126fn locking_lsp_erlang() {
127 let paths = crate::project_paths_at_current_directory_without_toml();
128 let lock = BuildLock::new_target(&paths, Mode::Lsp, Target::Erlang).expect("make lock");
129 let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
130 println!("Locked!")
131}
132
133#[test]
134fn locking_dev_javascript() {
135 let paths = crate::project_paths_at_current_directory_without_toml();
136 let lock = BuildLock::new_target(&paths, Mode::Dev, Target::JavaScript).expect("make lock");
137 let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
138 println!("Locked!")
139}
140
141#[test]
142fn locking_prod_javascript() {
143 let paths = crate::project_paths_at_current_directory_without_toml();
144 let lock = BuildLock::new_target(&paths, Mode::Prod, Target::JavaScript).expect("make lock");
145 let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
146 println!("Locked!")
147}
148
149#[test]
150fn locking_lsp_javascript() {
151 let paths = crate::project_paths_at_current_directory_without_toml();
152 let lock = BuildLock::new_target(&paths, Mode::Lsp, Target::JavaScript).expect("make lock");
153 let _guard1: Guard = lock.lock(&gleam_core::build::NullTelemetry).unwrap();
154 println!("Locked!")
155}