##// END OF EJS Templates
packaging: support building WiX installers with PyOxidizer...
packaging: support building WiX installers with PyOxidizer We initially implemented PyOxidizer support for Inno installers. That did most of the heavy work of integrating PyOxidizer into the packaging system. Implementing WiX installer support was pretty straightforward. Aspects of this patch look very similar to Inno's. The main difference is the handling of the Visual C++ Redistributable Runtime files. The WiX installer was formerly using merge modules to install the VC++ 9.0 runtime because this feature is supported by the WiX installer (it isn't easily available to Inno installers). Our strategy for the runtime files is to install the vcruntime140.dll file next to hg.exe just like any other file. While we could leverage WiX's functionality for invoking a VCRedist installer, I don't want to deal with the complexity at this juncture. So, we let run_pyoxidizer() copy vcruntime140.dll into the staging directory (like it does for Inno) and our dynamic WiX XML generator picks it up as a regular file and installs it. We did, however, have to teach mercurial.wxs how to conditionally use the merge modules. But this was rather straightforward. Comparing the file layout of the WiX installers before and after: * Various lib/*.{pyd, dll} files no longer exist * python27.dll was replaced by python37.dll * vcruntime140.dll was added All these changes are expected due to the transition to Python 3 and to PyOxidizer, which embeded the .pyd and .dll files in hg.exe. Differential Revision: https://phab.mercurial-scm.org/D8477

File last commit:

r45022:07d9fd60 default
r45260:c9517d9d default
Show More
files.rs
384 lines | 11.2 KiB | application/rls-services+xml | RustLexer
Raphaël Gomès
rust-docstrings: add missing module docstrings...
r42996 // files.rs
//
// Copyright 2019
// Raphaël Gomès <rgomes@octobus.net>,
// Yuya Nishihara <yuya@tcha.org>
//
// This software may be used and distributed according to the terms of the
// GNU General Public License version 2 or any later version.
//! Functions for fiddling with files.
Raphaël Gomès
rust-utils: add util for canonical path...
r44783 use crate::utils::{
hg_path::{path_to_hg_path_buf, HgPath, HgPathBuf, HgPathError},
path_auditor::PathAuditor,
replace_slice,
};
Raphaël Gomès
rust-pathauditor: add Rust implementation of the `pathauditor`...
r44737 use lazy_static::lazy_static;
Raphaël Gomès
rust-utils: add util for canonical path...
r44783 use same_file::is_same_file;
use std::borrow::ToOwned;
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 use std::fs::Metadata;
Raphaël Gomès
rust-dirs-multiset: add `DirsChildrenMultiset`...
r44739 use std::iter::FusedIterator;
Raphaël Gomès
rust-utils: add util for canonical path...
r44783 use std::ops::Deref;
use std::path::{Path, PathBuf};
Raphaël Gomès
rust-filepatterns: use bytes instead of String...
r42630
pub fn get_path_from_bytes(bytes: &[u8]) -> &Path {
let os_str;
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
os_str = std::ffi::OsStr::from_bytes(bytes);
}
Raphaël Gomès
rust-cross-platform: remove `unimplemented!` to get compile-time errors...
r43544 // TODO Handle other platforms
// TODO: convert from WTF8 to Windows MBCS (ANSI encoding).
// Perhaps, the return type would have to be Result<PathBuf>.
Raphaël Gomès
rust-filepatterns: use bytes instead of String...
r42630
Path::new(os_str)
}
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789
Yuya Nishihara
rust-cpython: do not convert warning pattern to utf-8 bytes...
r44320 // TODO: need to convert from WTF8 to MBCS bytes on Windows.
// that's why Vec<u8> is returned.
#[cfg(unix)]
pub fn get_bytes_from_path(path: impl AsRef<Path>) -> Vec<u8> {
use std::os::unix::ffi::OsStrExt;
path.as_ref().as_os_str().as_bytes().to_vec()
}
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 /// An iterator over repository path yielding itself and its ancestors.
#[derive(Copy, Clone, Debug)]
pub struct Ancestors<'a> {
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 next: Option<&'a HgPath>,
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 }
impl<'a> Iterator for Ancestors<'a> {
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 type Item = &'a HgPath;
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789
fn next(&mut self) -> Option<Self::Item> {
let next = self.next;
self.next = match self.next {
Some(s) if s.is_empty() => None,
Some(s) => {
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 let p = s.bytes().rposition(|c| *c == b'/').unwrap_or(0);
Some(HgPath::new(&s.as_bytes()[..p]))
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 }
None => None,
};
next
}
}
impl<'a> FusedIterator for Ancestors<'a> {}
Raphaël Gomès
rust-dirs-multiset: add `DirsChildrenMultiset`...
r44739 /// An iterator over repository path yielding itself and its ancestors.
#[derive(Copy, Clone, Debug)]
pub(crate) struct AncestorsWithBase<'a> {
next: Option<(&'a HgPath, &'a HgPath)>,
}
impl<'a> Iterator for AncestorsWithBase<'a> {
type Item = (&'a HgPath, &'a HgPath);
fn next(&mut self) -> Option<Self::Item> {
let next = self.next;
self.next = match self.next {
Some((s, _)) if s.is_empty() => None,
Some((s, _)) => Some(s.split_filename()),
None => None,
};
next
}
}
impl<'a> FusedIterator for AncestorsWithBase<'a> {}
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 /// Returns an iterator yielding ancestor directories of the given repository
/// path.
///
/// The path is separated by '/', and must not start with '/'.
///
/// The path itself isn't included unless it is b"" (meaning the root
/// directory.)
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 pub fn find_dirs<'a>(path: &'a HgPath) -> Ancestors<'a> {
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 let mut dirs = Ancestors { next: Some(path) };
if !path.is_empty() {
dirs.next(); // skip itself
}
dirs
}
Raphaël Gomès
rust-dirs-multiset: add `DirsChildrenMultiset`...
r44739 /// Returns an iterator yielding ancestor directories of the given repository
/// path.
///
/// The path is separated by '/', and must not start with '/'.
///
/// The path itself isn't included unless it is b"" (meaning the root
/// directory.)
pub(crate) fn find_dirs_with_base<'a>(
path: &'a HgPath,
) -> AncestorsWithBase<'a> {
let mut dirs = AncestorsWithBase {
next: Some((path, HgPath::new(b""))),
};
if !path.is_empty() {
dirs.next(); // skip itself
}
dirs
}
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 /// TODO more than ASCII?
pub fn normalize_case(path: &HgPath) -> HgPathBuf {
Raphaël Gomès
rust-utils: add normalize_case util to mirror Python one...
r43108 #[cfg(windows)] // NTFS compares via upper()
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 return path.to_ascii_uppercase();
Raphaël Gomès
rust-utils: add normalize_case util to mirror Python one...
r43108 #[cfg(unix)]
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 path.to_ascii_lowercase()
Raphaël Gomès
rust-utils: add normalize_case util to mirror Python one...
r43108 }
Raphaël Gomès
rust-pathauditor: add Rust implementation of the `pathauditor`...
r44737 lazy_static! {
static ref IGNORED_CHARS: Vec<Vec<u8>> = {
[
0x200c, 0x200d, 0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d,
0x202e, 0x206a, 0x206b, 0x206c, 0x206d, 0x206e, 0x206f, 0xfeff,
]
.iter()
.map(|code| {
std::char::from_u32(*code)
.unwrap()
.encode_utf8(&mut [0; 3])
.bytes()
.collect()
})
.collect()
};
}
fn hfs_ignore_clean(bytes: &[u8]) -> Vec<u8> {
let mut buf = bytes.to_owned();
let needs_escaping = bytes.iter().any(|b| *b == b'\xe2' || *b == b'\xef');
if needs_escaping {
for forbidden in IGNORED_CHARS.iter() {
replace_slice(&mut buf, forbidden, &[])
}
buf
} else {
buf
}
}
pub fn lower_clean(bytes: &[u8]) -> Vec<u8> {
hfs_ignore_clean(&bytes.to_ascii_lowercase())
}
Raphaël Gomès
rust-dirstate-status: add first Rust implementation of `dirstate.status`...
r43565 #[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct HgMetadata {
pub st_dev: u64,
pub st_mode: u32,
pub st_nlink: u64,
pub st_size: u64,
pub st_mtime: i64,
pub st_ctime: i64,
}
// TODO support other plaforms
#[cfg(unix)]
impl HgMetadata {
pub fn from_metadata(metadata: Metadata) -> Self {
use std::os::unix::fs::MetadataExt;
Self {
st_dev: metadata.dev(),
st_mode: metadata.mode(),
st_nlink: metadata.nlink(),
st_size: metadata.size(),
st_mtime: metadata.mtime(),
st_ctime: metadata.ctime(),
}
}
}
Raphaël Gomès
rust-utils: add util for canonical path...
r44783 /// Returns the canonical path of `name`, given `cwd` and `root`
pub fn canonical_path(
root: impl AsRef<Path>,
cwd: impl AsRef<Path>,
name: impl AsRef<Path>,
) -> Result<PathBuf, HgPathError> {
// TODO add missing normalization for other platforms
let root = root.as_ref();
let cwd = cwd.as_ref();
let name = name.as_ref();
let name = if !name.is_absolute() {
root.join(&cwd).join(&name)
} else {
name.to_owned()
};
Raphaël Gomès
rust-pathauditor: use interior mutability for use in multi-threaded contexts...
r45022 let auditor = PathAuditor::new(&root);
Raphaël Gomès
rust-utils: add util for canonical path...
r44783 if name != root && name.starts_with(&root) {
let name = name.strip_prefix(&root).unwrap();
auditor.audit_path(path_to_hg_path_buf(name)?)?;
return Ok(name.to_owned());
} else if name == root {
return Ok("".into());
} else {
// Determine whether `name' is in the hierarchy at or beneath `root',
// by iterating name=name.parent() until it returns `None` (can't
// check name == '/', because that doesn't work on windows).
let mut name = name.deref();
let original_name = name.to_owned();
loop {
let same = is_same_file(&name, &root).unwrap_or(false);
if same {
if name == original_name {
// `name` was actually the same as root (maybe a symlink)
return Ok("".into());
}
// `name` is a symlink to root, so `original_name` is under
// root
let rel_path = original_name.strip_prefix(&name).unwrap();
auditor.audit_path(path_to_hg_path_buf(&rel_path)?)?;
return Ok(rel_path.to_owned());
}
name = match name.parent() {
None => break,
Some(p) => p,
};
}
// TODO hint to the user about using --cwd
// Bubble up the responsibility to Python for now
Err(HgPathError::NotUnderRoot {
path: original_name.to_owned(),
root: root.to_owned(),
})
}
}
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 #[cfg(test)]
mod tests {
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 use super::*;
Raphaël Gomès
rust-utils: add util for canonical path...
r44783 use pretty_assertions::assert_eq;
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 #[test]
fn find_dirs_some() {
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 let mut dirs = super::find_dirs(HgPath::new(b"foo/bar/baz"));
assert_eq!(dirs.next(), Some(HgPath::new(b"foo/bar")));
assert_eq!(dirs.next(), Some(HgPath::new(b"foo")));
assert_eq!(dirs.next(), Some(HgPath::new(b"")));
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 assert_eq!(dirs.next(), None);
assert_eq!(dirs.next(), None);
}
#[test]
fn find_dirs_empty() {
Martin von Zweigbergk
utils: move finddirs() to pathutil...
r44032 // looks weird, but mercurial.pathutil.finddirs(b"") yields b""
Raphaël Gomès
rust-hgpath: replace all paths and filenames with HgPath/HgPathBuf...
r43227 let mut dirs = super::find_dirs(HgPath::new(b""));
assert_eq!(dirs.next(), Some(HgPath::new(b"")));
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 assert_eq!(dirs.next(), None);
assert_eq!(dirs.next(), None);
}
Raphaël Gomès
rust-dirs-multiset: add `DirsChildrenMultiset`...
r44739
#[test]
fn test_find_dirs_with_base_some() {
let mut dirs = super::find_dirs_with_base(HgPath::new(b"foo/bar/baz"));
assert_eq!(
dirs.next(),
Some((HgPath::new(b"foo/bar"), HgPath::new(b"baz")))
);
assert_eq!(
dirs.next(),
Some((HgPath::new(b"foo"), HgPath::new(b"bar")))
);
assert_eq!(dirs.next(), Some((HgPath::new(b""), HgPath::new(b"foo"))));
assert_eq!(dirs.next(), None);
assert_eq!(dirs.next(), None);
}
#[test]
fn test_find_dirs_with_base_empty() {
let mut dirs = super::find_dirs_with_base(HgPath::new(b""));
assert_eq!(dirs.next(), Some((HgPath::new(b""), HgPath::new(b""))));
assert_eq!(dirs.next(), None);
assert_eq!(dirs.next(), None);
}
Raphaël Gomès
rust-utils: add util for canonical path...
r44783
#[test]
fn test_canonical_path() {
let root = Path::new("/repo");
let cwd = Path::new("/dir");
let name = Path::new("filename");
assert_eq!(
canonical_path(root, cwd, name),
Err(HgPathError::NotUnderRoot {
path: PathBuf::from("/dir/filename"),
root: root.to_path_buf()
})
);
let root = Path::new("/repo");
let cwd = Path::new("/");
let name = Path::new("filename");
assert_eq!(
canonical_path(root, cwd, name),
Err(HgPathError::NotUnderRoot {
path: PathBuf::from("/filename"),
root: root.to_path_buf()
})
);
let root = Path::new("/repo");
let cwd = Path::new("/");
let name = Path::new("repo/filename");
assert_eq!(
canonical_path(root, cwd, name),
Ok(PathBuf::from("filename"))
);
let root = Path::new("/repo");
let cwd = Path::new("/repo");
let name = Path::new("filename");
assert_eq!(
canonical_path(root, cwd, name),
Ok(PathBuf::from("filename"))
);
let root = Path::new("/repo");
let cwd = Path::new("/repo/subdir");
let name = Path::new("filename");
assert_eq!(
canonical_path(root, cwd, name),
Ok(PathBuf::from("subdir/filename"))
);
}
#[test]
fn test_canonical_path_not_rooted() {
use std::fs::create_dir;
use tempfile::tempdir;
let base_dir = tempdir().unwrap();
let base_dir_path = base_dir.path();
let beneath_repo = base_dir_path.join("a");
let root = base_dir_path.join("a/b");
let out_of_repo = base_dir_path.join("c");
let under_repo_symlink = out_of_repo.join("d");
create_dir(&beneath_repo).unwrap();
create_dir(&root).unwrap();
// TODO make portable
std::os::unix::fs::symlink(&root, &out_of_repo).unwrap();
assert_eq!(
canonical_path(&root, Path::new(""), out_of_repo),
Ok(PathBuf::from(""))
);
assert_eq!(
canonical_path(&root, Path::new(""), &beneath_repo),
Err(HgPathError::NotUnderRoot {
path: beneath_repo.to_owned(),
root: root.to_owned()
})
);
assert_eq!(
canonical_path(&root, Path::new(""), &under_repo_symlink),
Ok(PathBuf::from("d"))
);
}
Yuya Nishihara
rust-dirstate: add helper to iterate ancestor paths...
r42789 }