Skip to content

Commit d3304ad

Browse files
committed
Build on windows
1 parent f4730d5 commit d3304ad

File tree

8 files changed

+83
-26
lines changed

8 files changed

+83
-26
lines changed

.envrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
source ~/.cargo/env

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
needs: fmt
6464
strategy:
6565
matrix:
66-
os: [ubuntu-latest, macos-latest]
66+
os: [ubuntu-latest, macos-latest, windows-latest]
6767
runs-on: ${{ matrix.os }}
6868
steps:
6969
- uses: actions/checkout@v4

crates/cli/src/execve.rs

+23
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
#[cfg(unix)]
12
use nix::unistd::execve as nix_execve;
3+
#[cfg(unix)]
24
use std::ffi::CString;
5+
36
use std::{collections::HashMap, error::Error};
47

8+
#[cfg(unix)]
59
pub fn execve(
610
cmd: String,
711
mut args: Vec<String>,
@@ -47,3 +51,22 @@ pub fn execve(
4751

4852
Ok(())
4953
}
54+
55+
#[cfg(windows)]
56+
use std::process::{exit, Command};
57+
58+
#[cfg(windows)]
59+
pub fn execve(
60+
cmd: String,
61+
args: Vec<String>,
62+
env: HashMap<String, String>,
63+
) -> Result<(), Box<dyn Error>> {
64+
Command::new(cmd)
65+
.args(args)
66+
.envs(env)
67+
.spawn()
68+
.expect("Failed to execute process");
69+
70+
// If you want behavior similar to `execve`, exit the current process
71+
exit(0);
72+
}

crates/lib/src/install.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ use tokio_util::compat::FuturesAsyncReadCompatExt;
1313
// futures::io::AsyncRead.
1414
use futures::stream::TryStreamExt;
1515

16+
#[cfg(windows)]
17+
use std::os::windows::fs::symlink_dir;
18+
1619
use crate::{
1720
cellar,
1821
client::build_client,
@@ -38,14 +41,22 @@ where
3841
{
3942
let shelf = config.pkgx_dir.join(&pkg.project);
4043
fs::create_dir_all(&shelf)?;
41-
let shelf = OpenOptions::new()
44+
45+
#[cfg(windows)]
46+
let lockfile = OpenOptions::new()
47+
.read(true)
48+
.write(true)
49+
.create(true)
50+
.open(shelf.join("lockfile"))?;
51+
#[cfg(not(windows))]
52+
let lockfile = OpenOptions::new()
4253
.read(true) // Open the directory in read-only mode
4354
.open(shelf.clone())?;
4455

4556
task::spawn_blocking({
46-
let shelf = shelf.try_clone()?;
57+
let lockfile = lockfile.try_clone()?;
4758
move || {
48-
shelf
59+
lockfile
4960
.lock_exclusive()
5061
.expect("unexpected error: install locking failed");
5162
}
@@ -57,7 +68,7 @@ where
5768
// did another instance of pkgx install us while we waited for the lock?
5869
// if so, we’re good: eject
5970
if dst_path.is_dir() {
60-
FileExt::unlock(&shelf)?;
71+
FileExt::unlock(&lockfile)?;
6172
return Ok(Installation {
6273
path: dst_path,
6374
pkg: pkg.clone(),
@@ -112,7 +123,7 @@ where
112123

113124
symlink(&installation, config).await?;
114125

115-
FileExt::unlock(&shelf)?;
126+
FileExt::unlock(&lockfile)?;
116127

117128
Ok(installation)
118129
}
@@ -203,9 +214,10 @@ async fn make_symlink(
203214
.file_name()
204215
.ok_or_else(|| anyhow::anyhow!("Could not get the base name of the installation path"))?;
205216

206-
match std::os::unix::fs::symlink(target, &symlink_path) {
207-
Ok(_) => Ok(()),
208-
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => Ok(()),
209-
Err(err) => Err(err.into()),
210-
}
217+
#[cfg(not(windows))]
218+
std::os::unix::fs::symlink(target, &symlink_path)?;
219+
#[cfg(windows)]
220+
symlink_dir(target, symlink_path)?;
221+
222+
Ok(())
211223
}

crates/lib/src/pantry.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,10 @@ impl<'de> Deserialize<'de> for Provides {
285285
ProvidesHelper::Map(map) => {
286286
#[cfg(target_os = "macos")]
287287
let key = "darwin";
288-
289288
#[cfg(target_os = "linux")]
290289
let key = "linux";
290+
#[cfg(windows)]
291+
let key = "windows";
291292

292293
if let Some(values) = map.get(key) {
293294
Ok(Provides(values.clone()))

crates/lib/src/sync.rs

+12-8
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,23 @@ async fn replace(config: &Config, conn: &mut Connection) -> Result<(), Box<dyn E
5252
config.dist_url,
5353
env!("PKGX_PANTRY_TARBALL_FILENAME")
5454
);
55-
let dest = &config.pantry_dir;
5655

57-
std::fs::create_dir_all(dest)?;
58-
let dir = OpenOptions::new()
59-
.read(true) // Open in read-only mode; no need to write.
60-
.open(dest)?;
61-
dir.lock_exclusive()?;
56+
std::fs::create_dir_all(&config.pantry_dir)?;
57+
#[cfg(not(windows))]
58+
let lockfile = OpenOptions::new().read(true).open(&config.pantry_dir)?;
59+
#[cfg(windows)]
60+
let lockfile = OpenOptions::new()
61+
.read(true)
62+
.create(true)
63+
.write(true)
64+
.open(config.pantry_dir.join("lockfile"))?;
65+
lockfile.lock_exclusive()?;
6266

63-
download_and_extract_pantry(&url, dest).await?;
67+
download_and_extract_pantry(&url, &config.pantry_dir).await?;
6468

6569
pantry_db::cache(config, conn)?;
6670

67-
FileExt::unlock(&dir)?;
71+
FileExt::unlock(&lockfile)?;
6872

6973
Ok(())
7074
}

crates/lib/src/types.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ impl Serialize for Installation {
8787
pub enum Host {
8888
Darwin,
8989
Linux,
90+
Windows,
9091
}
9192

9293
// These are only used per build at present
@@ -101,8 +102,8 @@ pub fn host() -> (Host, Arch) {
101102
let host = Host::Darwin;
102103
#[cfg(target_os = "linux")]
103104
let host = Host::Linux;
104-
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
105-
panic!("Unsupported platform");
105+
#[cfg(windows)]
106+
let host = Host::Windows;
106107

107108
#[cfg(target_arch = "aarch64")]
108109
let arch = Arch::Arm64;
@@ -119,6 +120,7 @@ impl fmt::Display for Host {
119120
let os_str = match self {
120121
Host::Linux => "linux",
121122
Host::Darwin => "darwin",
123+
Host::Windows => "windows",
122124
};
123125
write!(f, "{}", os_str)
124126
}

crates/lib/src/utils.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use std::{error::Error, os::unix::fs::PermissionsExt, path::Path};
1+
#[cfg(not(windows))]
2+
use std::os::unix::fs::PermissionsExt;
3+
use std::{error::Error, path::Path};
24

35
pub async fn find_program(arg: &str, paths: &Vec<String>) -> Result<String, Box<dyn Error>> {
46
if arg.starts_with("/") {
@@ -13,9 +15,21 @@ pub async fn find_program(arg: &str, paths: &Vec<String>) -> Result<String, Box<
1315
}
1416
for path in paths {
1517
let full_path = Path::new(&path).join(arg);
16-
if let Ok(metadata) = full_path.metadata() {
17-
if full_path.is_file() && (metadata.permissions().mode() & 0o111 != 0) {
18-
return Ok(full_path.to_str().unwrap().to_string());
18+
if full_path.is_file() {
19+
#[cfg(unix)]
20+
if let Ok(metadata) = full_path.metadata() {
21+
if metadata.permissions().mode() & 0o111 != 0 {
22+
return Ok(full_path.to_str().unwrap().to_string());
23+
}
24+
}
25+
#[cfg(windows)]
26+
if let Some(ext) = full_path.extension() {
27+
match ext.to_str() {
28+
Some("exe") | Some("bat") | Some("cmd") => {
29+
return Ok(full_path.to_str().unwrap().to_string())
30+
}
31+
_ => {}
32+
}
1933
}
2034
}
2135
}

0 commit comments

Comments
 (0)