diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..968187c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,29 @@ +{ + "terminal.integrated.profiles.windows": { + + "PowerShell": { + "source": "PowerShell", + "icon": "terminal-powershell" + }, + "Command Prompt": { + "path": [ + "${env:windir}\\Sysnative\\cmd.exe", + "${env:windir}\\System32\\cmd.exe" + ], + "args": [], + "icon": "terminal-cmd" + }, + "Git Bash": { + "source": "Git Bash" + } + }, + "rust-analyzer.linkedProjects": [ + ".\\Cargo.toml", + ".\\Cargo.toml", + ".\\Cargo.toml", + ".\\Cargo.toml", + ".\\Cargo.toml", + ".\\Cargo.toml" + ], + "rust-analyzer.showUnlinkedFileNotification": false +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 9087422..1c63f12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +clap = { version = "4.2.2", features = ["derive"] } diff --git a/README.md b/README.md index b18aa0d..2224f37 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,13 @@ # rust-cmaker #### 介绍 + +> 2023年了谁还在用传统cmake啊(大嘘) + 用rust封装MinGW的命令,方便一键在vscode中构建c/c++简单项目 -#### 软件架构 -软件架构说明 - - -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - #### 使用说明 -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +1. 将本程序加入到Path或者干脆在每一个c/c++项目的根目录中加入一个,反正它不大 +2. 使用`--help`命令行参数查看使用说明 +3. 测试版需要环境里已经有gcc、g++的环境变量 diff --git a/src/build_project/cmake_o.rs b/src/build_project/cmake_o.rs new file mode 100644 index 0000000..168f39d --- /dev/null +++ b/src/build_project/cmake_o.rs @@ -0,0 +1,46 @@ +use std::path::PathBuf; + +use super::config::BuildConfigTrait; + +pub fn cmake_o( + config: &Box, + src_path: PathBuf, + file_o_set: &mut Vec, +) -> Result<(), std::io::Error> { + // 遍历文件夹内每个子项 + let dir = std::fs::read_dir(src_path)?; + for path in dir { + let path = path?; + // 如果子项为文件夹, 递归查找内部项 + if path.file_type()?.is_dir() { + cmake_o(config, path.path(), file_o_set)?; + continue; + } + // 判断是否为需要编译的文件, 即.c/.cpp文件 + let file_path = path; + let file_name = String::from(file_path.file_name().to_str().unwrap()); + let file_type = super::utils::file_is_c_or_cpp(&file_name); + if file_type.is_else() { + continue; + } + // 找到需要编译的文件, 将其输出到target/o目录下 + let new_file_path = + PathBuf::from(config.getpath_target_o()).join((*file_o_set).len().to_string() + ".o"); + + use super::gcc::make_file_to_target_o; + match file_type { + super::utils::FileType::C => { + make_file_to_target_o(config.getpath_gcc(), &file_path.path(), &new_file_path)?; + } + super::utils::FileType::Cpp => { + make_file_to_target_o(config.getpath_gpp(), &file_path.path(), &new_file_path)?; + } + super::utils::FileType::Else => { + continue; + } + } + + file_o_set.push(new_file_path); + } + return Ok(()); +} diff --git a/src/build_project/config.rs b/src/build_project/config.rs new file mode 100644 index 0000000..0077a72 --- /dev/null +++ b/src/build_project/config.rs @@ -0,0 +1,89 @@ +use std::path::PathBuf; + +pub trait BuildConfigTrait { + fn getpath_gcc(&self) -> String; + fn getpath_gpp(&self) -> String; + fn getpath_src(&self) -> String; + fn getpath_target_o(&self) -> String; + fn get_project_name(&self) -> &str; + fn getpath_target_bin(&self) -> String; + fn getpath_target_file(&self) -> String; +} + +#[derive(Debug)] +pub struct BuildConfig { + compiler_path: Option, + project_path: PathBuf, + project_name: String, +} + +impl BuildConfig { + pub fn new( + compiler_path: Option, + project_path: PathBuf, + project_name: String, + ) -> Self { + Self { + compiler_path, + project_path, + project_name, + } + } +} + +impl BuildConfigTrait for BuildConfig { + fn getpath_gcc(&self) -> String { + return self.getpath_gpp(); // 此处需要修改 + } + + fn getpath_gpp(&self) -> String { + if self.compiler_path.is_none() { + return String::from("g++"); + } + String::from( + self.compiler_path + .clone() + .unwrap() + .join("bin") + .join("g++") + .to_str() + .unwrap(), + ) + } + + fn getpath_src(&self) -> String { + String::from(self.project_path.clone().join("src").to_str().unwrap()) + } + + fn getpath_target_o(&self) -> String { + String::from( + self.project_path + .clone() + .join("target") + .join("o") + .to_str() + .unwrap(), + ) + } + + fn get_project_name(&self) -> &str { + &self.project_name + } + + fn getpath_target_bin(&self) -> String { + String::from( + self.project_path + .clone() + .join("target") + .join("bin") + .to_str() + .unwrap(), + ) + } + + fn getpath_target_file(&self) -> String { + let file_name = String::from(self.get_project_name()) + ".exe"; + let ans = PathBuf::from(self.getpath_target_bin()).join(file_name); + String::from(ans.to_str().unwrap()) + } +} diff --git a/src/build_project/gcc.rs b/src/build_project/gcc.rs new file mode 100644 index 0000000..17a235e --- /dev/null +++ b/src/build_project/gcc.rs @@ -0,0 +1,65 @@ +use std::path::{Path, PathBuf}; + +// 根据单个.c/.cpp文件的路径创建.o文件并输出到new_file_path +pub fn make_file_to_target_o( + command: String, + source_file_path: &PathBuf, + new_file_path: &PathBuf, +) -> Result<(), std::io::Error> { + // 获取路径 + let source_file_path = source_file_path.to_str().unwrap(); + let new_file_path = new_file_path.to_str().unwrap(); + + // 运行指令 + let mut output = std::process::Command::new(command); + output.args(["-o", new_file_path]); + output.args(["-c", source_file_path]); + let output = output.output()?; + + // 输出调试信息 + let state = if output.status.success() { + format!("{}: success to compile 编译成功", crate::PROJECT_NAME) + } else { + format!( + "{}: failed to compile 编译失败 \n错误信息: {:?}", + crate::PROJECT_NAME, + String::from_utf8(output.stderr).unwrap() + ) + }; + println!( + "{}: file : {:?} ---> new file : {:?} ---> {}", + crate::PROJECT_NAME, + Path::new(source_file_path).file_name().unwrap(), + Path::new(new_file_path).file_name().unwrap(), + state + ); + Ok(()) +} + +// 根据.o文件的集合构建可执行程序 +pub fn make_o_files_to_bin( + command: String, + source_paths: &mut Vec, + target_path: String, +) -> Result<(), std::io::Error> { + // 创建指令 + let mut output = std::process::Command::new(command); + while !source_paths.is_empty() { + let item = source_paths.pop().unwrap(); + output.arg(item.to_str().unwrap()); + } + output.args(["-o", &target_path]); + + // 运行指令 + let output = output.output()?; + if output.status.success() { + println!("{}: success to compile 编译成功", crate::PROJECT_NAME); + } else { + println!( + "{}: failed to compile 编译失败\n{:?}", + crate::PROJECT_NAME, + output + ); + } + Ok(()) +} diff --git a/src/build_project/mod.rs b/src/build_project/mod.rs new file mode 100644 index 0000000..36c52d9 --- /dev/null +++ b/src/build_project/mod.rs @@ -0,0 +1,49 @@ +mod cmake_o; +mod config; +mod gcc; +mod utils; + +use std::path::PathBuf; + +use config::{BuildConfig, BuildConfigTrait}; + +pub fn build_project() { + let project_path = std::env::current_dir().unwrap(); + println!("{:?}", project_path); + + // 获取配置 + let config: Box = Box::new(BuildConfig::new( + None, + // Some(PathBuf::from(r"C:\Program Files\CodeBlocks\MinGW")), + PathBuf::from(project_path), + String::from("HelloWorld"), + )); + + // 递归所有src目录下的.c/.cpp文件并编译为.o文件输出到@/target/o目录下 + let mut file_set: Vec = Vec::new(); // 所有应临时存储的文件路径集合 + println!( + "{}: start to compile 开始编译 step: 1/2", + crate::PROJECT_NAME + ); + std::fs::create_dir_all(config.getpath_target_o()).unwrap(); // 先创建这层目录 + cmake_o::cmake_o(&config, PathBuf::from(config.getpath_src()), &mut file_set).unwrap(); + + // 遍历@/target/o联合编译到@/target/bin中 + println!( + "{}: start to compile 开始编译 step: 2/2", + crate::PROJECT_NAME + ); + std::fs::create_dir_all(config.getpath_target_bin()).unwrap(); // 先创建这层目录 + if PathBuf::from(config.getpath_target_file()).exists() { + std::fs::remove_file(config.getpath_target_file()).unwrap(); // 先删除目标文件 + } + gcc::make_o_files_to_bin( + config.getpath_gpp(), + &mut file_set, + config.getpath_target_file(), + ) + .unwrap(); + + // 删除@/target/o文件夹 + std::fs::remove_dir_all(config.getpath_target_o()).unwrap(); +} diff --git a/src/build_project/utils.rs b/src/build_project/utils.rs new file mode 100644 index 0000000..9913264 --- /dev/null +++ b/src/build_project/utils.rs @@ -0,0 +1,35 @@ +pub enum FileType { + C, + Cpp, + Else, +} + +impl FileType { + pub fn is_else(&self) -> bool { + match self { + Self::Else => { + return true; + } + _ => { + return false; + } + } + } +} + +// 判断文件名是否为.c或.cpp结尾 +pub fn file_is_c_or_cpp(file_name: &String) -> FileType { + let t: Vec<&u8> = file_name.as_bytes().iter().collect(); + if t.len() >= 2 && *t[t.len() - 2] == b'.' && *t[t.len() - 1] == b'c' { + return FileType::C; + } + if t.len() >= 4 + && *t[t.len() - 4] == b'.' + && *t[t.len() - 3] == b'c' + && *t[t.len() - 2] == b'p' + && *t[t.len() - 1] == b'p' + { + return FileType::Cpp; + } + return FileType::Else; +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..ce8c53c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,42 @@ -fn main() { - println!("Hello, world!"); +pub mod build_project; +pub mod new_project; + +pub const PROJECT_NAME: &str = "rust-cmaker"; + +use clap::{command, Parser, Subcommand}; + +/// 命令行参数 +#[derive(Parser, Debug)] +#[command(author = "Cloyir min123456@foxmail.com", version = "1.0.0", about = None, long_about = None)] +struct Args { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand, Debug)] +enum Commands { + /// 在当前目录新建项目 + New { + /// 项目名称 + project_name: String, + }, + /// 在此项目内编译 + Build, +} + +fn main() -> std::io::Result<()> { + // 解析命令行参数 + let args = Args::parse(); + // println!("args: {:?}", args); + + match args.command { + Commands::New { project_name } => { + new_project::new_project(project_name); + } + Commands::Build => { + build_project::build_project(); + } + } + + Ok(()) } diff --git a/src/new_project/mod.rs b/src/new_project/mod.rs new file mode 100644 index 0000000..fb31232 --- /dev/null +++ b/src/new_project/mod.rs @@ -0,0 +1,23 @@ +mod text; + +/// 创建新项目 +pub fn new_project(project_name: String) { + println!("新建项目: {}", project_name); + let target_path = std::env::current_dir().unwrap().join(&project_name); + println!("目标路径: {:?}", target_path); + if let Ok(_) = target_path.read_dir() { + println!("创建项目失败, 已存在名为{}的文件夹", project_name); + return; + } + let src_path = target_path.join("src"); + std::fs::create_dir_all(&src_path).unwrap(); + + // 新建README.md文件 + std::fs::write(target_path.join("README.md"), text::get_readme_text()).unwrap(); + + // 新建main.cpp文件 + std::fs::write(src_path.join("main.cpp"), text::get_maincpp_text()).unwrap(); + + println!("创建成功"); + println!("请进入{}文件夹再使用build指令构建项目", project_name); +} diff --git a/src/new_project/text.rs b/src/new_project/text.rs new file mode 100644 index 0000000..dcf894e --- /dev/null +++ b/src/new_project/text.rs @@ -0,0 +1,14 @@ +// 返回生成文件的文本 + +pub fn get_readme_text() -> &'static str { + "" +} + +pub fn get_maincpp_text() -> &'static str { + r##"#include + +int main() { + std::cout << "Hello World!" << std::endl; +} +"## +}