Skip to content

Commit 7d6365f

Browse files
committed
windows support
1 parent a506406 commit 7d6365f

File tree

10 files changed

+139
-57
lines changed

10 files changed

+139
-57
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@ env:
1313
jobs:
1414
ci:
1515
name: Check
16-
runs-on: ubuntu-latest
16+
runs-on: ${{ matrix.os }}
17+
strategy:
18+
matrix:
19+
os: [ubuntu-latest, windows-latest, macos-latest]
1720
steps:
1821
- uses: actions/checkout@v4
1922
- uses: actions-rust-lang/setup-rust-toolchain@v1
2023
- run: cargo fmt --all --check
2124
- run: cargo clippy --all-targets --all-features
2225
- run: cargo test
26+
- run: cargo build --verbose
27+
- run: cargo build --release --verbose
28+
- run: cargo build --profile dist --verbose

Cargo.lock

Lines changed: 32 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ ignore = "0.4.22"
3333
itertools = "0.12.1"
3434
env_logger = "0.11.2"
3535
log = "0.4.20"
36-
uzers = "0.11.3"
3736
config = "0.14.0"
3837
serde_derive = "1.0.196"
3938
directories = "5.0.1"
4039
git2 = { version = "0.18.2", default-features = false }
40+
whoami = "1.4.1"
41+
42+
[target.'cfg(windows)'.dependencies]
43+
file-id = "0.2.1"
4144

4245
[dev-dependencies]
4346
tempfile = "3.10.0"
@@ -67,7 +70,7 @@ cargo-dist-version = "0.10.0"
6770
# The installers to generate for each app
6871
installers = ["shell", "powershell", "msi"]
6972
# Target platforms to build apps for (Rust target-triple syntax)
70-
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"]
73+
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"]
7174
# CI backends to support
7275
ci = ["github"]
7376
# Publish jobs to run in CI

src/build.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
use crate::fsutils;
1+
use crate::fsutils::{compute_checksum, size_from_meta};
22
use crate::hash::file_md5;
33
use crate::objects::{Object, Tree, TreeEntry};
44
use crate::odb::Odb;
55
use crate::state::{State, StateHash, StateValue};
6+
use crate::timeutils::unix_time;
67
use ignore;
78
use ignore::gitignore::Gitignore;
89
use jwalk::{Parallelism, WalkDir};
910
use log::debug;
1011
use rayon::prelude::*;
11-
use std::fs;
12-
use std::os::unix::fs::MetadataExt;
1312
use std::path::{Path, PathBuf};
1413
use std::time::Instant;
14+
use std::{fs, u128};
1515
struct FileInfo {
1616
checksum: String,
1717
path: PathBuf,
@@ -20,9 +20,38 @@ struct FileInfo {
2020

2121
impl FileInfo {
2222
fn from_metadata(path: &Path, meta: &fs::Metadata) -> Self {
23+
let ut = unix_time(meta.modified().unwrap()).unwrap();
24+
let ino: u128 = {
25+
#[cfg(unix)]
26+
{
27+
use std::os::unix::fs::MetadataExt;
28+
u128::from(meta.ino())
29+
}
30+
31+
#[cfg(windows)]
32+
{
33+
use file_id::{get_file_id, FileId};
34+
match get_file_id(path).unwrap() {
35+
FileId::LowRes {
36+
volume_serial_number: _,
37+
file_index,
38+
} => u128::from(file_index),
39+
FileId::HighRes {
40+
volume_serial_number: _,
41+
file_id: id,
42+
} => id,
43+
FileId::Inode {
44+
device_id: _,
45+
inode_number,
46+
} => u128::from(inode_number),
47+
}
48+
}
49+
};
50+
let size = size_from_meta(meta);
51+
let checksum = compute_checksum(ut, ino, size);
2352
Self {
24-
checksum: fsutils::checksum_from_metadata(meta),
25-
size: meta.size(),
53+
checksum,
54+
size,
2655
path: path.to_path_buf(),
2756
}
2857
}

src/fsutils.rs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
use crate::hash::md5;
2+
use core::panic;
23
use std::fs;
3-
use std::os::unix::fs::{MetadataExt, PermissionsExt};
4-
use std::path::PathBuf;
4+
use std::path::{Path, PathBuf};
55

6-
pub fn checksum_from_metadata(meta: &fs::Metadata) -> String {
7-
#[allow(clippy::cast_precision_loss)]
8-
let m = meta.mtime() as f64 + (meta.mtime_nsec() as f64 / 1_000_000_000f64);
6+
pub fn compute_checksum(ut: f64, ino: u128, size: u64) -> String {
97
let st = "([".to_owned()
10-
+ &meta.ino().to_string()
8+
+ &ino.to_string()
119
+ ", "
12-
+ &m.to_string()
10+
+ &ut.to_string()
1311
+ ", "
14-
+ &meta.size().to_string()
12+
+ &size.to_string()
1513
+ "],)";
1614
let hash = md5(&mut st.as_bytes());
1715
u128::from_str_radix(&hash, 16).unwrap().to_string()
1816
}
1917

20-
pub fn checksum(path: &PathBuf) -> String {
21-
let meta = fs::metadata(path).unwrap();
22-
checksum_from_metadata(&meta)
18+
#[cfg(unix)]
19+
pub fn size_from_meta(meta: &fs::Metadata) -> u64 {
20+
use std::os::unix::fs::MetadataExt;
21+
meta.size()
2322
}
2423

25-
pub fn size(path: PathBuf) -> u64 {
26-
let meta = fs::metadata(path).unwrap();
27-
meta.size()
24+
#[cfg(windows)]
25+
pub fn size_from_meta(meta: &fs::Metadata) -> u64 {
26+
use std::os::windows::fs::MetadataExt;
27+
meta.file_size()
2828
}
2929

3030
pub fn transfer_file(from: &PathBuf, to: &PathBuf) {
@@ -33,7 +33,8 @@ pub fn transfer_file(from: &PathBuf, to: &PathBuf) {
3333
.unwrap_or_else(|_| panic!("transfer failed: {from:?} {to:?}"));
3434
}
3535

36-
pub fn protect_file(path: &PathBuf) {
37-
let permission = fs::Permissions::from_mode(0o444);
38-
fs::set_permissions(path, permission).unwrap_or_default();
36+
pub fn protect_file(path: &Path) {
37+
if let Ok(m) = path.metadata() {
38+
m.permissions().set_readonly(true);
39+
}
3940
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod odb;
1313
pub mod repo;
1414
pub mod state;
1515
pub mod status;
16+
pub mod timeutils;
1617
pub mod transfer;
1718

1819
pub use build::build;

src/objects.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use crate::hash::md5;
22
use crate::json_format;
3-
use serde::{Deserialize, Serialize};
3+
use itertools::Itertools;
4+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
45
use std::convert::From;
56
use std::error::Error;
67
use std::fs::File;
78
use std::io::BufReader;
8-
use std::path::PathBuf;
9+
use std::path::{Path, PathBuf};
910

1011
#[derive(Clone)]
1112
pub enum Object {
@@ -17,6 +18,7 @@ pub type HashFile = String;
1718

1819
#[derive(Deserialize, Clone, PartialEq, Debug, PartialOrd, Ord, Eq)]
1920
pub struct TreeEntry {
21+
#[serde(deserialize_with = "posixstr_to_ospath")]
2022
pub relpath: PathBuf,
2123
#[serde(rename = "md5")]
2224
pub oid: String,
@@ -27,6 +29,7 @@ pub struct TreeEntry {
2729
struct TreeEntrySerializer {
2830
#[serde(rename = "md5")]
2931
pub oid: String,
32+
#[serde(serialize_with = "posixify_path")]
3033
pub relpath: PathBuf,
3134
}
3235

@@ -54,6 +57,21 @@ impl Serialize for TreeEntry {
5457
}
5558
}
5659

60+
fn posixify_path<S>(x: &Path, s: S) -> Result<S::Ok, S::Error>
61+
where
62+
S: Serializer,
63+
{
64+
s.serialize_str(&x.iter().map(|p| p.to_str().unwrap()).join("/"))
65+
}
66+
67+
fn posixstr_to_ospath<'de, D>(deserializer: D) -> Result<PathBuf, D::Error>
68+
where
69+
D: Deserializer<'de>,
70+
{
71+
let s: &str = Deserialize::deserialize(deserializer)?;
72+
Ok(s.split('/').collect())
73+
}
74+
5775
impl Tree {
5876
pub fn serialize(&self) -> serde_json::Result<String> {
5977
// make it compatible with `json.dumps()` separator

src/repo.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::ffi::OsString;
2-
use std::os::unix::fs::MetadataExt;
32
use std::path::{Path, PathBuf};
43

54
use log::debug;
@@ -8,6 +7,7 @@ use crate::config::Config;
87
use crate::hash::md5;
98
use crate::odb::Odb;
109
use crate::state::State;
10+
use crate::timeutils::unix_time;
1111
use std::error::Error;
1212
use std::fs;
1313
use std::{env, io};
@@ -32,25 +32,23 @@ fn btime(tmp_dir: &Path) -> Result<f64, io::Error> {
3232

3333
match result {
3434
Ok(()) => match fs::metadata(btime) {
35-
#[allow(clippy::cast_precision_loss)]
36-
Ok(meta) => Ok(meta.mtime() as f64 + (meta.mtime_nsec() as f64 / 1_000_000_000f64)),
35+
Ok(meta) => Ok(unix_time(meta.modified()?).unwrap()),
3736
Err(e) => Err(e),
3837
},
3938
Err(e) => Err(e),
4039
}
4140
}
4241

43-
#[cfg(unix)]
4442
fn db_dirname(root: &Path, tmp_dir: &Path) -> String {
45-
use std::os::unix::ffi::OsStrExt;
4643
let btime = btime(tmp_dir).unwrap();
47-
let user = uzers::get_current_username().unwrap();
44+
let user = whoami::username();
4845
let dvc_major = 3;
49-
let salt = 2;
46+
let salt = 0;
5047

5148
let mut st: OsString = "('".into();
5249
st.push(root.as_os_str());
5350
st.push("', ");
51+
st.push("None, "); // subdir
5452
st.push(btime.to_string());
5553
st.push(", '");
5654
st.push(user);
@@ -60,7 +58,7 @@ fn db_dirname(root: &Path, tmp_dir: &Path) -> String {
6058
st.push(salt.to_string());
6159
st.push(")");
6260

63-
md5(&mut st.as_bytes())
61+
md5(&mut st.as_encoded_bytes())
6462
}
6563

6664
#[cfg(not(any(
@@ -73,6 +71,12 @@ fn db_dirs() -> PathBuf {
7371
"/var/tmp/dvc/repo".into()
7472
}
7573

74+
#[cfg(target_os = "windows")]
75+
fn db_dirs() -> PathBuf {
76+
Path::new(&env::var("CSIDL_COMMON_APPDATA").unwrap_or("C:/ProgramData/iterative/dvc".into()))
77+
.to_owned()
78+
}
79+
7680
#[cfg(target_os = "macos")]
7781
fn db_dirs() -> PathBuf {
7882
"/Library/Caches/dvc/repo".into()

0 commit comments

Comments
 (0)