diff --git a/Cargo.toml b/Cargo.toml index 68ab918df..16df7e16f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,27 +4,31 @@ name = "maturin" version = "1.4.0" description = "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" exclude = [ - "test-crates/**/*", - "sysconfig/*", - "test-data/*", - "ci/*", - "tests/*", - "guide/*", - ".github/*", - ".devcontainer/*", - "Dockerfile", - ".dockerignore", - "deny.toml", - "noxfile.py", - "test-dockerfile.sh", - "Code-of-Conduct.md", + "test-crates/**/*", + "sysconfig/*", + "test-data/*", + "ci/*", + "tests/*", + "guide/*", + ".github/*", + ".devcontainer/*", + "Dockerfile", + ".dockerignore", + "deny.toml", + "noxfile.py", + "test-dockerfile.sh", + "Code-of-Conduct.md", ] homepage = "https://github.com/pyo3/maturin" readme = "README.md" repository = "https://github.com/pyo3/maturin" license = "MIT OR Apache-2.0" keywords = ["python", "cffi", "packaging", "pypi", "pyo3"] -categories = ["api-bindings", "development-tools::ffi", "command-line-utilities"] +categories = [ + "api-bindings", + "development-tools::ffi", + "command-line-utilities", +] edition = "2021" rust-version = "1.70" @@ -53,7 +57,11 @@ tar = "0.4.38" tempfile = "3.2.0" toml = "0.8.8" toml_edit = "0.21.0" -zip = { version = "0.6.1", default-features = false, features = ["bzip2", "deflate", "time"] } +zip = { version = "0.6.1", default-features = false, features = [ + "bzip2", + "deflate", + "time", +] } thiserror = "1.0.37" fs-err = "2.11.0" fat-macho = { version = "0.4.8", default-features = false } @@ -75,10 +83,15 @@ path-slash = "0.2.1" pep440_rs = { version = "0.5.0", features = ["serde", "tracing"] } pep508_rs = { version = "0.4.2", features = ["serde", "tracing"] } time = "0.3.17" -unicode-xid = "0.2.4" +unicode-xid = { version = "0.2.4", optional = true } # cli -clap = { version = "4.0.0", features = ["derive", "env", "wrap_help", "unstable-styles"] } +clap = { version = "4.0.0", features = [ + "derive", + "env", + "wrap_help", + "unstable-styles", +] } clap_complete_command = { version = "0.5.1", optional = true } # cross compile @@ -87,7 +100,9 @@ cargo-xwin = { version = "0.16.4", default-features = false, optional = true } # log tracing = "0.1.36" -tracing-subscriber = { version = "0.3.15", features = ["env-filter"], optional = true } +tracing-subscriber = { version = "0.3.15", features = [ + "env-filter", +], optional = true } # project scaffolding, maturin new/init/generate-ci dialoguer = { version = "0.11.0", default-features = false, optional = true } @@ -98,12 +113,20 @@ minijinja = { version = "1.0.7", optional = true } bytesize = { version = "1.0.1", optional = true } configparser = { version = "3.0.3", optional = true } dirs = { version = "5.0.0", optional = true } -multipart = { version = "0.18.0", features = ["client"], default-features = false, optional = true } -ureq = { version = "2.9.4", features = ["gzip", "json", "socks-proxy"], default-features = false, optional = true } +multipart = { version = "0.18.0", features = [ + "client", +], default-features = false, optional = true } +ureq = { version = "2.9.4", features = [ + "gzip", + "json", + "socks-proxy", +], default-features = false, optional = true } native-tls = { version = "0.2.8", optional = true } rustls = { version = "0.22.2", optional = true } rustls-pemfile = { version = "2.0.0", optional = true } -keyring = { version = "2.0.0", default-features = false, features = ["linux-no-secret-service"], optional = true } +keyring = { version = "2.0.0", default-features = false, features = [ + "linux-no-secret-service", +], optional = true } wild = { version = "2.1.0", optional = true } url = { version = "2.3.0", optional = true } @@ -125,12 +148,31 @@ log = ["tracing-subscriber"] cli-completion = ["dep:clap_complete_command"] -upload = ["ureq", "multipart", "configparser", "bytesize", "dialoguer/password", "url", "wild", "dep:dirs"] +upload = [ + "ureq", + "multipart", + "configparser", + "bytesize", + "dialoguer/password", + "url", + "wild", + "dep:dirs", +] # keyring doesn't support *BSD so it's not enabled in `full` by default password-storage = ["upload", "keyring"] -rustls = ["dep:rustls", "ureq?/tls", "cargo-xwin?/rustls-tls", "dep:rustls-pemfile"] -native-tls = ["dep:native-tls", "ureq?/native-tls", "cargo-xwin?/native-tls", "dep:rustls-pemfile"] +rustls = [ + "dep:rustls", + "ureq?/tls", + "cargo-xwin?/rustls-tls", + "dep:rustls-pemfile", +] +native-tls = [ + "dep:native-tls", + "ureq?/native-tls", + "cargo-xwin?/native-tls", + "dep:rustls-pemfile", +] # cross compile using zig or xwin cross-compile = ["zig", "xwin"] @@ -138,7 +180,7 @@ zig = ["cargo-zigbuild"] xwin = ["cargo-xwin"] # project scaffolding -scaffolding = ["dialoguer", "console", "minijinja"] +scaffolding = ["dialoguer", "console", "minijinja", "unicode-xid"] # Internal feature to speed up the tests significantly faster-tests = [] diff --git a/src/lib.rs b/src/lib.rs index e6fcc644c..dc89994e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,7 +55,6 @@ mod metadata; mod module_writer; #[cfg(feature = "scaffolding")] mod new_project; -mod package_name_validations; mod project_layout; pub mod pyproject_toml; mod python_interpreter; diff --git a/src/new_project.rs b/src/new_project.rs index 4ec938246..90683d663 100644 --- a/src/new_project.rs +++ b/src/new_project.rs @@ -1,5 +1,5 @@ +use self::package_name_validations::cargo_check_name; use crate::ci::GenerateCI; -use crate::package_name_validations::cargo_check_name; use crate::BridgeModel; use anyhow::{bail, Context, Result}; use console::style; @@ -256,3 +256,123 @@ fn generate_project( let generator = ProjectGenerator::new(name, layout, bindings, overwrite)?; generator.generate(project_path) } + +mod package_name_validations { + // Based on: https://github.com/rust-lang/cargo/blob/e975158c1b542aa3833fd8584746538c17a6ae55/src/cargo/ops/cargo_new.rs#L169 + pub fn cargo_check_name(name: &str) -> anyhow::Result<()> { + // Instead of `PackageName::new` which performs these checks in the original cargo code + validate_package_name(name)?; + + if is_keyword(name) { + anyhow::bail!( + "the name `{}` cannot be used as a package name, it is a Rust keyword", + name, + ); + } + if is_conflicting_artifact_name(name) { + anyhow::bail!( + "the name `{}` cannot be used as a package name, \ + it conflicts with cargo's build directory names", + name, + ); + } + if name == "test" { + anyhow::bail!( + "the name `test` cannot be used as a package name, \ + it conflicts with Rust's built-in test library", + ); + } + if ["core", "std", "alloc", "proc_macro", "proc-macro"].contains(&name) { + eprintln!( + "⚠️ Warning: the name `{}` is part of Rust's standard library\n\ + It is recommended to use a different name to avoid problems.", + name, + ); + } + if is_windows_reserved(name) { + eprintln!( + "⚠️ Warning: the name `{}` is a reserved Windows filename\n\ + This package will not work on Windows platforms.", + name + ); + } + if is_non_ascii_name(name) { + eprintln!( + "⚠️ Warning: the name `{}` contains non-ASCII characters\n\ + Non-ASCII crate names are not supported by Rust.", + name + ); + } + let name_in_lowercase = name.to_lowercase(); + if name != name_in_lowercase { + eprintln!( + "⚠️ Warning: the name `{name}` is not snake_case or kebab-case which is recommended for package names, consider `{name_in_lowercase}`" + ); + } + + Ok(()) + } + + // Based on: https://github.com/rust-lang/cargo/blob/7b7af3077bff8d60b7f124189bc9de227d3063a9/crates/cargo-util-schemas/src/restricted_names.rs#L42 + fn validate_package_name(name: &str) -> anyhow::Result<()> { + if name.is_empty() { + anyhow::bail!("Package names cannot be empty"); + } + + let mut chars = name.chars(); + if let Some(ch) = chars.next() { + if ch.is_ascii_digit() { + // A specific error for a potentially common case. + anyhow::bail!("Package names cannot start with a digit"); + } + if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') { + anyhow::bail!( + "the first character must be a Unicode XID start character (most letters or `_`)" + ); + } + } + for ch in chars { + if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-') { + anyhow::bail!( + "characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)" + ); + } + } + Ok(()) + } + + // The following functions are based on https://github.com/rust-lang/cargo/blob/e975158c1b542aa3833fd8584746538c17a6ae55/src/cargo/util/restricted_names.rs + + /// Returns `true` if the name contains non-ASCII characters. + pub fn is_non_ascii_name(name: &str) -> bool { + name.chars().any(|ch| ch > '\x7f') + } + + /// A Rust keyword. + pub fn is_keyword(name: &str) -> bool { + // See https://doc.rust-lang.org/reference/keywords.html + [ + "Self", "abstract", "as", "async", "await", "become", "box", "break", "const", + "continue", "crate", "do", "dyn", "else", "enum", "extern", "false", "final", "fn", + "for", "if", "impl", "in", "let", "loop", "macro", "match", "mod", "move", "mut", + "override", "priv", "pub", "ref", "return", "self", "static", "struct", "super", + "trait", "true", "try", "type", "typeof", "unsafe", "unsized", "use", "virtual", + "where", "while", "yield", + ] + .contains(&name) + } + + /// These names cannot be used on Windows, even with an extension. + pub fn is_windows_reserved(name: &str) -> bool { + [ + "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", + "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", + ] + .contains(&name.to_ascii_lowercase().as_str()) + } + + /// An artifact with this name will conflict with one of Cargo's build directories. + pub fn is_conflicting_artifact_name(name: &str) -> bool { + ["deps", "examples", "build", "incremental"].contains(&name) + } +} diff --git a/src/package_name_validations.rs b/src/package_name_validations.rs deleted file mode 100644 index 05db1f0e1..000000000 --- a/src/package_name_validations.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Based on: https://github.com/rust-lang/cargo/blob/e975158c1b542aa3833fd8584746538c17a6ae55/src/cargo/ops/cargo_new.rs#L169 -pub fn cargo_check_name(name: &str) -> anyhow::Result<()> { - // Instead of `PackageName::new` which performs these checks in the original cargo code - validate_package_name(name)?; - - if is_keyword(name) { - anyhow::bail!( - "the name `{}` cannot be used as a package name, it is a Rust keyword", - name, - ); - } - if is_conflicting_artifact_name(name) { - anyhow::bail!( - "the name `{}` cannot be used as a package name, \ - it conflicts with cargo's build directory names", - name, - ); - } - if name == "test" { - anyhow::bail!( - "the name `test` cannot be used as a package name, \ - it conflicts with Rust's built-in test library", - ); - } - if ["core", "std", "alloc", "proc_macro", "proc-macro"].contains(&name) { - eprintln!( - "⚠️ Warning: the name `{}` is part of Rust's standard library\n\ - It is recommended to use a different name to avoid problems.", - name, - ); - } - if is_windows_reserved(name) { - eprintln!( - "⚠️ Warning: the name `{}` is a reserved Windows filename\n\ - This package will not work on Windows platforms.", - name - ); - } - if is_non_ascii_name(name) { - eprintln!( - "⚠️ Warning: the name `{}` contains non-ASCII characters\n\ - Non-ASCII crate names are not supported by Rust.", - name - ); - } - let name_in_lowercase = name.to_lowercase(); - if name != name_in_lowercase { - eprintln!( - "⚠️ Warning: the name `{name}` is not snake_case or kebab-case which is recommended for package names, consider `{name_in_lowercase}`" - ); - } - - Ok(()) -} - -// Based on: https://github.com/rust-lang/cargo/blob/7b7af3077bff8d60b7f124189bc9de227d3063a9/crates/cargo-util-schemas/src/restricted_names.rs#L42 -fn validate_package_name(name: &str) -> anyhow::Result<()> { - if name.is_empty() { - anyhow::bail!("Package names cannot be empty"); - } - - let mut chars = name.chars(); - if let Some(ch) = chars.next() { - if ch.is_ascii_digit() { - // A specific error for a potentially common case. - anyhow::bail!("Package names cannot start with a digit"); - } - if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') { - anyhow::bail!( - "the first character must be a Unicode XID start character (most letters or `_`)" - ); - } - } - for ch in chars { - if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-') { - anyhow::bail!( - "characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)" - ); - } - } - Ok(()) -} - -// The following functions are based on https://github.com/rust-lang/cargo/blob/e975158c1b542aa3833fd8584746538c17a6ae55/src/cargo/util/restricted_names.rs - -/// Returns `true` if the name contains non-ASCII characters. -pub fn is_non_ascii_name(name: &str) -> bool { - name.chars().any(|ch| ch > '\x7f') -} - -/// A Rust keyword. -pub fn is_keyword(name: &str) -> bool { - // See https://doc.rust-lang.org/reference/keywords.html - [ - "Self", "abstract", "as", "async", "await", "become", "box", "break", "const", "continue", - "crate", "do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "if", - "impl", "in", "let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", - "pub", "ref", "return", "self", "static", "struct", "super", "trait", "true", "try", - "type", "typeof", "unsafe", "unsized", "use", "virtual", "where", "while", "yield", - ] - .contains(&name) -} - -/// These names cannot be used on Windows, even with an extension. -pub fn is_windows_reserved(name: &str) -> bool { - [ - "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", - "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", - ] - .contains(&name.to_ascii_lowercase().as_str()) -} - -/// An artifact with this name will conflict with one of Cargo's build directories. -pub fn is_conflicting_artifact_name(name: &str) -> bool { - ["deps", "examples", "build", "incremental"].contains(&name) -}