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