Skip to content

Commit

Permalink
init: new command similar to cargo init
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Nov 30, 2021
1 parent fc039bd commit 2779f14
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 53 deletions.
4 changes: 3 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

* Add a `maturin init` command as a companion to `maturin new` in [#719](https://github.com/PyO3/maturin/pull/719)

## [0.12.3] - 2021-11-29

* Use platform tag from `sysconfig.platform` on non-portable Linux in [#709](https://github.com/PyO3/maturin/pull/709)
Expand Down Expand Up @@ -122,7 +124,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.10.3] - 2021-04-13

* The `upload` command is now implemented, it is mostly similar to `twine upload`. [#484](https://github.com/PyO3/maturin/pull/484)
* Interpreter search now uses python 3.6 to 3.12
* Interpreter search now uses python 3.6 to 3.12
* Add basic support for OpenBSD in [#496](https://github.com/PyO3/maturin/pull/496)
* Fix the PowerPC platform by messense in [#503](https://github.com/PyO3/maturin/pull/503)

Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub use crate::metadata::{Metadata21, WheelMetadata};
pub use crate::module_writer::{
write_dist_info, ModuleWriter, PathWriter, SDistWriter, WheelWriter,
};
pub use crate::new_project::new_project;
pub use crate::new_project::{init_project, new_project, GenerateProjectOptions};
pub use crate::pyproject_toml::PyProjectToml;
pub use crate::python_interpreter::PythonInterpreter;
pub use crate::target::Target;
Expand Down
33 changes: 16 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ use fs_err as fs;
use human_panic::setup_panic;
#[cfg(feature = "password-storage")]
use keyring::{Keyring, KeyringError};
use maturin::GenerateProjectOptions;
use maturin::{
develop, new_project, source_distribution, write_dist_info, BridgeModel, BuildOptions,
CargoToml, Metadata21, PathWriter, PlatformTag, PyProjectToml, PythonInterpreter, Target,
develop, init_project, new_project, source_distribution, write_dist_info, BridgeModel,
BuildOptions, CargoToml, Metadata21, PathWriter, PlatformTag, PyProjectToml, PythonInterpreter,
Target,
};
use std::env;
use std::io;
Expand Down Expand Up @@ -270,20 +272,21 @@ enum Opt {
#[structopt(short, long, parse(from_os_str))]
out: Option<PathBuf>,
},
/// Create a new cargo project in an existing directory
#[structopt(name = "init")]
InitProject {
/// Project path
path: Option<String>,
#[structopt(flatten)]
options: GenerateProjectOptions,
},
/// Create a new cargo project
#[structopt(name = "new")]
NewProject {
/// Project path
path: String,
/// Set the resulting package name, defaults to the directory name
#[structopt(long)]
name: Option<String>,
/// Use mixed Rust/Python project layout
#[structopt(long)]
mixed: bool,
/// Which kind of bindings to use
#[structopt(short, long, possible_values = &["pyo3", "rust-cpython", "cffi", "bin"])]
bindings: Option<String>,
#[structopt(flatten)]
options: GenerateProjectOptions,
},
/// Uploads python packages to pypi
///
Expand Down Expand Up @@ -636,12 +639,8 @@ fn run() -> Result<()> {
.context("Failed to build source distribution")?;
}
Opt::Pep517(subcommand) => pep517(subcommand)?,
Opt::NewProject {
path,
name,
mixed,
bindings,
} => new_project(path, name, mixed, bindings)?,
Opt::InitProject { path, options } => init_project(path, options)?,
Opt::NewProject { path, options } => new_project(path, options)?,
#[cfg(feature = "upload")]
Opt::Upload { publish, files } => {
if files.is_empty() {
Expand Down
105 changes: 71 additions & 34 deletions src/new_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ use dialoguer::{theme::ColorfulTheme, Select};
use fs_err as fs;
use minijinja::{context, Environment};
use std::path::Path;
use structopt::StructOpt;

struct ProjectGenerator<'a> {
env: Environment<'a>,
project_name: String,
crate_name: String,
bindings: String,
mixed: bool,
overwrite: bool,
}

impl<'a> ProjectGenerator<'a> {
fn new(project_name: String, mixed: bool, bindings: String) -> Result<Self> {
fn new(project_name: String, mixed: bool, bindings: String, overwrite: bool) -> Result<Self> {
let crate_name = project_name.replace('-', "_");
let mut env = Environment::new();
env.add_template(".gitignore", include_str!("templates/.gitignore.j2"))?;
Expand All @@ -33,48 +35,34 @@ impl<'a> ProjectGenerator<'a> {
crate_name,
bindings,
mixed,
overwrite,
})
}

fn generate(&self, project_path: &Path) -> Result<()> {
let src_path = project_path.join("src");
fs::create_dir_all(&src_path)?;

let gitignore = self.render_template(".gitignore")?;
fs::write(project_path.join(".gitignore"), gitignore)?;

let cargo_toml = self.render_template("Cargo.toml")?;
fs::write(project_path.join("Cargo.toml"), cargo_toml)?;

let pyproject_toml = self.render_template("pyproject.toml")?;
fs::write(project_path.join("pyproject.toml"), pyproject_toml)?;
self.write_project_file(project_path, ".gitignore")?;
self.write_project_file(project_path, "Cargo.toml")?;
self.write_project_file(project_path, "pyproject.toml")?;

if self.bindings == "bin" {
let main_rs = self.render_template("main.rs")?;
fs::write(src_path.join("main.rs"), main_rs)?;
self.write_project_file(&src_path, "main.rs")?;
} else {
let lib_rs = self.render_template("lib.rs")?;
fs::write(src_path.join("lib.rs"), lib_rs)?;
self.write_project_file(&src_path, "lib.rs")?;
}

let gh_action_path = project_path.join(".github").join("workflows");
fs::create_dir_all(&gh_action_path)?;
let ci_yml = self.render_template("CI.yml")?;
fs::write(gh_action_path.join("CI.yml"), ci_yml)?;
self.write_project_file(&gh_action_path, "CI.yml")?;

if self.mixed {
let py_path = project_path.join(&self.crate_name);
fs::create_dir_all(&py_path)?;
let init_py = self.render_template("__init__.py")?;
fs::write(py_path.join("__init__.py"), init_py)?;
self.write_project_file(&py_path, "__init__.py")?;
}

println!(
" ✨ {} {} {}",
style("Done!").bold().green(),
style("New project created").bold(),
style(&project_path.display()).underlined()
);
Ok(())
}

Expand All @@ -91,37 +79,86 @@ impl<'a> ProjectGenerator<'a> {
))?;
Ok(out)
}

fn write_project_file(&self, directory: &Path, file: &str) -> Result<()> {
let path = directory.join(file);
if self.overwrite || !path.exists() {
fs::write(path, self.render_template(file)?)?;
}
Ok(())
}
}

/// Generate a new cargo project
pub fn new_project(
path: String,
#[derive(Debug, StructOpt)]
/// Options common to `maturin new` and `maturin init`.
pub struct GenerateProjectOptions {
/// Set the resulting package name, defaults to the directory name
#[structopt(long)]
name: Option<String>,
/// Use mixed Rust/Python project layout
#[structopt(long)]
mixed: bool,
/// Which kind of bindings to use
#[structopt(short, long, possible_values = &["pyo3", "rust-cpython", "cffi", "bin"])]
bindings: Option<String>,
) -> Result<()> {
}

/// Generate a new cargo project
pub fn new_project(path: String, options: GenerateProjectOptions) -> Result<()> {
let project_path = Path::new(&path);
if project_path.exists() {
bail!("destination `{}` already exists", project_path.display());
}
generate_project(project_path, options, true)?;
println!(
" ✨ {} {} {}",
style("Done!").bold().green(),
style("New project created").bold(),
style(&project_path.display()).underlined()
);
Ok(())
}

/// Generate a new cargo project in an existing directory
pub fn init_project(path: Option<String>, options: GenerateProjectOptions) -> Result<()> {
let project_path = path
.map(Into::into)
.map_or_else(std::env::current_dir, Ok)?;
if project_path.join("pyproject.toml").exists() || project_path.join("Cargo.toml").exists() {
bail!("`maturin init` cannot be run on existing projects");
}
generate_project(&project_path, options, false)?;
println!(
" ✨ {} {} {}",
style("Done!").bold().green(),
style("Initialized project").bold(),
style(&project_path.display()).underlined()
);
Ok(())
}

let name = if let Some(name) = name {
fn generate_project(
project_path: &Path,
options: GenerateProjectOptions,
overwrite: bool,
) -> Result<()> {
let name = if let Some(name) = options.name {
name
} else {
let file_name = project_path
.file_name()
.context("Fail to get name from path")?;
let file_name = project_path.file_name().with_context(|| {
format!("Failed to get name from path '{}'", project_path.display())
})?;
file_name
.to_str()
.context("Filename isn't valid Unicode")?
.to_string()
};
let bindings_items = if mixed {
let bindings_items = if options.mixed {
vec!["pyo3", "rust-cpython", "cffi"]
} else {
vec!["pyo3", "rust-cpython", "cffi", "bin"]
};
let bindings = if let Some(bindings) = bindings {
let bindings = if let Some(bindings) = options.bindings {
bindings
} else {
let selection = Select::with_theme(&ColorfulTheme::default())
Expand All @@ -135,6 +172,6 @@ pub fn new_project(
bindings_items[selection].to_string()
};

let generator = ProjectGenerator::new(name, mixed, bindings)?;
let generator = ProjectGenerator::new(name, options.mixed, bindings, overwrite)?;
generator.generate(project_path)
}

0 comments on commit 2779f14

Please sign in to comment.