Compare commits

...

8 Commits

20 changed files with 540 additions and 432 deletions

View File

@ -7,3 +7,5 @@ edition = "2021"
[dependencies]
clap = { version = "4.2.2", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@ -4,27 +4,39 @@
> 2023年了谁还在用传统cmake啊大嘘
用rust封装MinGW的命令方便一键在vscode中构建c/c++简单项目灵感来自cargo
用rust封装MinGW的`gcc/g++`命令方便一键在vscode中构建c/c++简单项目,灵感来自rust的包管理器cargo
现在是开发初期
#### 快速使用
1. 将本程序加入到Path、塞到MinGW/bin目录下甚至是用路径直接使用, 这个随你
* 没错你最好安装一个MinGW并将其的bin目录放到你的环境变量里
2. 或许你会觉得它的名字过长了, 可以给它改一个你喜欢的简称, 比如 `rcm` , 之后我们就都用这个名字了
3. 找到你放c/c++项目的根目录, 输入 `rcm new project` 创建一个名为`project`的新项目
4. 进入生成的project文件夹, 用vscode打开它(`cd project`, `code .`)
5. 在src目录下编写你的程序, 默认已经生成好了一个main.cpp
6. 使用`rcm build -r`构建程序并运行, 或者你需要`rcm build -o 3`进行一个o3优化
7. 如果你没有配置好MinGW的环境变量, 你需要显式指定MinGW文件夹的路径, 详情见`rcm build -h`
8. 目前只能构建目录下`src/`文件夹里的 .c/.cpp 文件
1. 将本项目找个文件夹放好
* 然后将本项目的bin文件夹添加到你的环境变量中
2. 将本项目的bin目录加入到你的环境变量
* 目前`bin`目录下应该只有一个名为`rcm`的可执行程序
* 本项目构建出来的程序就是这个程序
3. 找到你放c/c++项目的根目录, 输入 `rcm new project` 以default为模板创建一个名为`project`的新项目
4. 进入生成的project文件夹, 用vscode打开它(`cd project`; `code .`)
5. 在src目录下编写你的程序
6. 你可以显式指定`MinGW`文件夹的路径, 详情见`rcm build -h`,你或许可以用这个实现交叉编译
7. 目前只能构建目录下`src/`文件夹里的 .c/.cpp 文件
8. 默认统一使用`g++`指令来编译
#### 已知问题
1. 代码是赶出来的, 写的依托
2. 没有链接库的功能
3. 不能判断哪些文件是修改过的, 所以只能全部编译一遍, 很耗时, 而且不知道怎么修
4. 只能指定目录下`src/`文件夹里的内容来编译以及只能输出到`target/bin/`目录下, 之后或许会增加新的编译选项
5. 需要手动配置环境变量
6. 统一的用g++进行编译, 应该判断如果全是.c的文件就用gcc编译
1. 代码写的依托
2. 不能判断哪些文件是修改过的, 所以只能全部编译一遍, 很耗时, 而且大概率以后也不会改
3. 只能指定目录下`src/`文件夹里的内容来编译以及只能输出到`target/bin/`目录下, 之后或许会增加新的编译选项
* 可能会影响到写学校的作业,但管他呢
4. 需要手动配置环境变量
#### rcm项目构建说明
1. 默认情况下, rcm只会构建`root/src`目录下的文件, 你应当将项目中的代码都放在此文件夹下, 这样你可以在根目录下放点别的, 比如一份`README.md`文档
2. 如果`root/src`中仅有一份`.c``.cpp`文件, 则会将它视为入口直接编译为一份二进制程序
3. `root/src`中同级目录下不应存在任何文件名相同但扩展名不同的文件, 比如`main.cpp``main.c`不应同时出现在`root/src`目录下
4. 默认情况下, 如果要构建二进制程序, src目录下应该放有一份`main.cpp``main.c`作为入口, 你应该在这里放置主函数
5. 默认情况下, `root/src/bin`下的每一个`.c/.cpp`文件都会独立作为入口构建一份二进制程序, 每个入口程序间互不干扰绕
6. 在构建时会忽略`*/test`文件夹下的所有文件, 这样你可以写一点测试
7. rcm会将项目根目录下的lib文件夹内的所有以`lib`开头以`.a`结尾的文件作为静态链接库一起编译
8. 推荐在`root/src`的每个文件夹(包括`src`文件夹它自己)下放一份`mod.h`文件, 引用这个`mod.h`文件就可以声明该目录下所有应包括的头文件, 包括子目录。这样构建的库项目会很清晰

View File

@ -1,46 +0,0 @@
use std::path::PathBuf;
use super::config::BuildConfigTrait;
pub fn cmake_o(
config: &Box<dyn BuildConfigTrait>,
src_path: PathBuf,
file_o_set: &mut Vec<PathBuf>,
) -> 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(());
}

View File

@ -1,118 +0,0 @@
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;
fn get_extra_final_args(&self) -> &Vec<String>;
fn need_run(&self) -> bool;
}
#[derive(Debug)]
pub struct BuildConfig {
compiler_path: Option<PathBuf>,
project_path: PathBuf,
project_name: String,
extra_final_args: Vec<String>,
need_run: bool,
}
impl BuildConfig {
pub fn new(
compiler_path: Option<PathBuf>,
project_name: String,
build_options: i32,
run: bool,
) -> Self {
let project_path = std::env::current_dir().unwrap();
let mut extra = Vec::new();
match build_options {
1 => {
extra.push(String::from("-O1"));
}
2 => {
extra.push(String::from("-O2"));
}
3 => {
extra.push(String::from("-O3"));
}
_ => {}
}
Self {
compiler_path,
project_path,
project_name,
extra_final_args: extra,
need_run: run,
}
}
}
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 = super::utils::make_app_file_name(self.get_project_name());
let ans = PathBuf::from(self.getpath_target_bin()).join(file_name);
String::from(ans.to_str().unwrap())
}
fn get_extra_final_args(&self) -> &Vec<String> {
return &self.extra_final_args;
}
fn need_run(&self) -> bool {
return self.need_run;
}
}

View File

@ -1,69 +0,0 @@
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<PathBuf>,
target_path: String,
extra_args: &Vec<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());
}
for item in extra_args {
output.arg(item);
}
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(())
}

View File

@ -1,59 +0,0 @@
mod cmake_o;
mod config;
mod gcc;
mod utils;
use std::path::PathBuf;
use config::{BuildConfig, BuildConfigTrait};
pub fn build_project(
build_options: i32,
run: bool,
mingw: Option<PathBuf>,
) -> Result<(), std::io::Error> {
let project_path = std::env::current_dir().unwrap();
println!("{:?}", project_path);
// 获取配置
let config: Box<dyn BuildConfigTrait> = Box::new(BuildConfig::new(
mingw,
String::from("main"),
build_options,
run,
));
println!("{}: start to compile 开始编译", crate::PROJECT_NAME);
// 递归所有src目录下的.c/.cpp文件并编译为.o文件输出到@/target/o目录下
let mut file_set: Vec<PathBuf> = Vec::new(); // 所有应临时存储的文件路径集合
std::fs::create_dir_all(config.getpath_target_o()).unwrap(); // 先创建这层目录
cmake_o::cmake_o(&config, PathBuf::from(config.getpath_src()), &mut file_set)?;
// 遍历@/target/o联合编译到@/target/bin中
std::fs::create_dir_all(config.getpath_target_bin())?; // 先创建这层目录
if PathBuf::from(config.getpath_target_file()).exists() {
std::fs::remove_file(config.getpath_target_file())?; // 先删除目标文件
}
gcc::make_o_files_to_bin(
config.getpath_gpp(),
&mut file_set,
config.getpath_target_file(),
config.get_extra_final_args(),
)?;
// 删除@/target/o文件夹
std::fs::remove_dir_all(config.getpath_target_o())?;
// 构建完之后是否要立即运行
if config.need_run() {
println!("{}: 开始执行...", crate::PROJECT_NAME);
let mut command = std::process::Command::new(config.getpath_target_file());
command.stdin(std::process::Stdio::inherit());
command.stdout(std::process::Stdio::inherit());
command.output()?;
println!("{}: 执行完毕...", crate::PROJECT_NAME);
}
Ok(())
}

View File

@ -1,43 +0,0 @@
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;
}
// 生成可执行程序的文件名(仅windows上有.exe结尾)
pub fn make_app_file_name(file_name: &str) -> String {
if cfg!(target_os = "windows") {
return String::from(file_name) + ".exe";
}
return String::from(file_name);
}

12
src/cadd/mod.rs Normal file
View File

@ -0,0 +1,12 @@
use clap::Parser;
#[derive(Parser, Debug)]
pub struct CommandsAdd {
/// 项目名称
#[clap(name = "lib_name")]
lib_name: String,
}
pub fn run(_config: CommandsAdd) -> Result<(), std::io::Error> {
Ok(())
}

17
src/cbuild/command.rs Normal file
View File

@ -0,0 +1,17 @@
use std::process::Command;
use crate::rtools::config::RConfig;
use super::CommandsBuild;
/// 得到可能带有路径名的gcc/g++指令
pub fn get_gcc_command(config: &CommandsBuild, r_config: &RConfig) -> Command {
let gcc = if config.c { "gcc" } else { "g++" };
if let Some(bin_path) = r_config.get_bin_path() {
let command: String = bin_path.join(gcc).into_os_string().into_string().unwrap();
Command::new(&command)
} else {
Command::new(gcc)
}
}

125
src/cbuild/files.rs Normal file
View File

@ -0,0 +1,125 @@
use std::path::PathBuf;
#[derive(Debug)]
pub struct File {
pub path: PathBuf,
pub name: String,
pub extension: String,
}
impl File {
pub fn new(path: PathBuf, name: String, extension: String) -> Self {
Self {
path,
name,
extension,
}
}
pub fn new_by_path(path: PathBuf) -> Self {
let name = path.file_name().unwrap().to_string_lossy().into_owned(); // 带扩展名的文件
let extension = std::path::Path::new(&path).extension().unwrap();
let extension = extension.to_string_lossy().into_owned();
let name = String::from(
std::path::Path::new(&name)
.file_stem()
.unwrap()
.to_str()
.unwrap(),
);
Self::new(path, name, extension)
}
}
#[derive(Debug)]
pub struct FilesSet {
/// 构建的入口文件
pub entry_files: Vec<File>,
/// 入口文件以外的.c/.cpp文件
pub c_files: Vec<File>,
/// .h/.hpp文件
pub h_files: Vec<File>,
}
impl FilesSet {
/// 输入root/src的路径, 整理该路径下的所有文件
pub fn read_src(path: PathBuf) -> Self {
let mut entry_files = Vec::new();
let mut c_files = Vec::new();
let mut h_files = Vec::new();
// 将src文件夹单独处理, 逻辑和fileset_read_dir差不多
for file in std::fs::read_dir(&path).unwrap() {
let file = file.unwrap();
if file.file_type().unwrap().is_dir() {
let name = file.file_name().to_string_lossy().into_owned();
// 忽略test文件夹
if name == "test" {
continue;
}
// 将src/bin文件夹的所有.c/.cpp文件视作入口文件
// 将src下其它文件夹视作正常.c/.cpp文件
if name == "bin" {
let new_path = path.join("bin");
fileset_read_dir(&new_path, &mut entry_files, &mut h_files).unwrap();
} else {
let new_path = path.join(name);
fileset_read_dir(&new_path, &mut c_files, &mut h_files).unwrap();
}
} else {
// 如果是文件
let file = File::new_by_path(file.path());
if file.extension == "c" || file.extension == "cpp" {
if file.name == "main" {
entry_files.push(file);
} else {
c_files.push(file);
}
} else if file.extension == "h" || file.extension == "hpp" {
h_files.push(file);
}
}
}
// 如果src目录下仅有一个.c/.cpp文件, 则视为唯一项目入口
if entry_files.len() == 0 && c_files.len() == 1 {
let t = entry_files;
entry_files = c_files;
c_files = t;
}
Self {
entry_files,
c_files,
h_files,
}
}
}
/// 将路径文件夹内所有文件分组整理到集合中
fn fileset_read_dir(
path: &PathBuf,
c_files: &mut Vec<File>,
h_files: &mut Vec<File>,
) -> std::io::Result<()> {
for file in std::fs::read_dir(path)? {
let file = file?;
if file.file_type()?.is_dir() {
let name = file.file_name().to_string_lossy().into_owned();
// 忽略test文件夹
if name == "test" {
continue;
}
let new_path = path.join(name);
fileset_read_dir(&new_path, c_files, h_files)?;
} else {
let file = File::new_by_path(file.path());
if file.extension == "c" || file.extension == "cpp" {
c_files.push(file);
} else if file.extension == "h" || file.extension == "hpp" {
h_files.push(file);
}
}
}
Ok(())
}

174
src/cbuild/mod.rs Normal file
View File

@ -0,0 +1,174 @@
mod command;
mod files;
use std::path::PathBuf;
use crate::const_value::console_log;
use self::files::FilesSet;
#[derive(clap::Parser, Debug)]
pub struct CommandsBuild {
/// 静默执行
#[clap(short, long, default_value = "false")]
quiet: bool,
/// 编译后是否立即运行
#[clap(short, long, default_value = "false")]
run: bool,
/// MinGW编译器地址, 不填则会从rcm的config文件中查找
#[clap(short, long, default_value = None)]
mingw: Option<String>,
/// 采用gcc编译指令而不是g++
#[clap(short, default_value = "false")]
c: bool,
/// 其它自定义选项, 需要在语句前加'.', 举例: --other ".-O3 .-mwindows"
#[clap(long)]
other: Option<String>,
}
pub fn run(config: CommandsBuild) -> Result<(), std::io::Error> {
// 获取项目路径
let project_path = std::env::current_dir().unwrap();
// 获取生成文件的目标路径
// let bin_path = project_path.join("target").join("bin");
let bin_path = project_path.join("target");
std::fs::create_dir_all(&bin_path).unwrap(); // 确保bin目录存在
if !config.quiet {
console_log(&format!("项目路径{:?}", project_path));
console_log(&format!("start to compile 开始编译"));
}
// 将src目录下的文件整理归纳
let files: FilesSet = FilesSet::read_src(project_path.join("src"));
// build指令需要读取rust-cmaker下的config文件
let r_config = crate::rtools::config::RConfig::read();
let mut need_run = None;
// 命令通用部分
let current_args: Vec<String> = make_current_args(&config, &files, project_path.join("lib"));
// 将每一个entry_file构建到指定目录下
for entry_file in &files.entry_files {
let mut gcc_cmd = command::get_gcc_command(&config, &r_config);
// 指定生成文件名称
gcc_cmd.arg("-o").arg({
let mut target_name = entry_file.name.clone();
if cfg!(target_os = "windows") {
target_name += ".exe";
}
let target_path = bin_path.join(target_name);
target_path
});
// 添加入口文件
gcc_cmd.arg(&entry_file.path);
// 组装通用命令
for item in &current_args {
gcc_cmd.arg(item);
}
// 执行命令
let output = gcc_cmd.output();
match output {
Ok(output) => {
if !output.status.success() {
if !config.quiet {
let msg = String::from_utf8_lossy(&output.stderr);
console_log(&format!("编译失败: {}", msg));
}
continue;
} else if !config.quiet {
console_log(&format!(
"编译成功: {}.{}",
entry_file.name, entry_file.extension
));
}
}
Err(err) => {
if !config.quiet {
console_log(&format!("编译失败: {:?}", err));
}
continue;
}
}
// 构建完之后判断是否要立即运行
if config.run && (files.entry_files.len() == 1 || entry_file.name == "main") {
need_run = Some(entry_file);
}
}
// 如果满足条件, 运行需要立即执行的程序
if let Some(run_file) = need_run {
if !config.quiet {
console_log(&format!("{}开始执行: ", run_file.name));
}
let mut command: std::process::Command =
std::process::Command::new(bin_path.join(&run_file.name));
command.stdin(std::process::Stdio::inherit());
command.stdout(std::process::Stdio::inherit());
command.stderr(std::process::Stdio::inherit());
command.output()?;
if !config.quiet {
console_log(&format!("{}执行完毕: ", run_file.name));
}
}
Ok(())
}
fn make_current_args(config: &CommandsBuild, files: &FilesSet, lib_path: PathBuf) -> Vec<String> {
let mut current_args: Vec<String> = Vec::new();
// 添加所有非入口文件
for c_file in &files.c_files {
let arg = String::from(&c_file.path.clone().into_os_string().into_string().unwrap());
current_args.push(arg);
}
// 从project/lib文件夹里获取所有的.a文件作为静态链接库
if let Ok(entries) = std::fs::read_dir(&lib_path) {
let mut a_files = Vec::new();
for entry in entries {
if let Ok(entry) = &entry {
let file_path = entry.path();
if file_path.is_file() {
let file = files::File::new_by_path(file_path);
if file.extension == "a" && file.name.starts_with("lib") {
a_files.push(file);
}
}
}
}
if a_files.len() != 0 {
current_args.push(String::from("-L"));
current_args.push(lib_path.into_os_string().into_string().to_owned().unwrap());
for item in a_files {
let ans = item.name.replacen("lib", "-l", 1);
current_args.push(ans);
}
}
}
// 组装config.other
if let Some(other) = &config.other {
for option in other.split_whitespace() {
let op: String = option.chars().skip(1).collect(); // 去除前置'.'
current_args.push(op);
}
}
current_args
}

45
src/cnew/mod.rs Normal file
View File

@ -0,0 +1,45 @@
use clap::Parser;
use crate::{const_value::console_log, rtools};
#[derive(Parser, Debug)]
pub struct CommandsNew {
/// 项目名称
#[clap(name = "new_project_name")]
project_name: String,
/// 静默执行
#[clap(short, long, default_value = "false")]
quiet: bool,
/// 从自定义模板构建
#[clap(short, long, name = "template_name", default_value = "default")]
template: String,
}
/// 创建新项目
pub fn run(config: CommandsNew) -> Result<(), std::io::Error> {
let target_path = std::env::current_dir()?.join(&config.project_name);
let mould_path = rtools::get_home_path().join("mould").join(&config.template);
if let Ok(_) = target_path.read_dir() {
if !config.quiet {
console_log(&format!(
"创建项目失败, 已存在名为{}的文件夹",
config.project_name
));
}
return Ok(());
}
if !config.quiet {
console_log(&format!("新建项目: {}", config.project_name));
console_log(&format!("目标路径: {:?}", target_path));
console_log(&format!("模板名称: {:?}", config.template));
}
// 从mould文件夹下获取模板并复制到目标路径
rtools::copy_dir(&mould_path, &target_path)?;
Ok(())
}

22
src/const_value.rs Normal file
View File

@ -0,0 +1,22 @@
// 定义一些全局常数变量
/// 本项目的名称
pub const THIS_PROJECT_NAME: &str = "rust-cmaker";
/// 以本项目名称发言: rust-cmaker: xxxx
pub fn console_log(msg: &str) {
println!("{}: {}", THIS_PROJECT_NAME, msg);
}
/// 作者信息
pub const AUTHOR: &str = "Cloyir mine123456@foxmail.com";
/// 项目版本
pub const VERSION: &str = "v0.0.1-alpha";
/// -h 指令写在标头的内容
pub const ABOUT: &str = "rcm——一个类cargo的c/c++项目构建管理工具";
/// --help 指令写在标头的内容
pub const LONG_ABOUT: &str =
"rust-cmaker——一个类cargo的c/c++项目构建管理工具\n什么年代了还在用传统cmake?";

12
src/cstore/mod.rs Normal file
View File

@ -0,0 +1,12 @@
use clap::Parser;
#[derive(Parser, Debug)]
pub struct CommandsStore {
/// 指定静态库名称
#[clap(short, long)]
name: Option<String>,
}
pub fn run(_config: CommandsStore) -> Result<(), std::io::Error> {
Ok(())
}

12
src/ctest/mod.rs Normal file
View File

@ -0,0 +1,12 @@
use clap::Parser;
#[derive(Parser, Debug)]
pub struct CommandsTest {
/// 每完成一个测试暂停一次
#[clap(short, long, default_value = "false")]
pause: bool,
}
pub fn run(_config: CommandsTest) -> Result<(), std::io::Error> {
Ok(())
}

View File

@ -1,15 +1,18 @@
pub mod build_project;
pub mod new_project;
pub mod const_value;
pub const PROJECT_NAME: &str = "rust-cmaker";
use std::path::PathBuf;
mod cadd;
mod cbuild;
mod cnew;
mod cstore;
mod ctest;
mod rtools;
use clap::{command, Parser};
use const_value::{ABOUT, AUTHOR, LONG_ABOUT, VERSION};
/// 命令行参数
#[derive(Parser, Debug)]
#[command(author = "Cloyir mine123456@foxmail.com", version = "1.0.0", about = None, long_about = None)]
#[command(author = AUTHOR, version = VERSION, about = ABOUT, long_about = LONG_ABOUT)]
struct Args {
#[command(subcommand)]
command: Commands,
@ -18,51 +21,28 @@ struct Args {
#[derive(Parser, Debug)]
enum Commands {
/// 在当前目录新建项目
#[command(about = "创建一个新项目")]
New {
/// 项目名称
#[clap(name = "new_project_name")]
project_name: String,
},
/// 在此项目内编译
#[command(about = "构建当前文件夹下的项目")]
Build {
/// -O优化选项
#[clap(short = 'o', long = "option", name = "0~3", default_value = "0")]
build_options: i32,
/// 编译后是否立即运行
#[clap(short, long, default_value = "false")]
run: bool,
/// MinGW编译器地址, 不填默认已配置为环境变量
#[clap(short, long, default_value = None)]
mingw: Option<String>,
},
New(cnew::CommandsNew),
/// 构建当前目录下的项目
Build(cbuild::CommandsBuild),
/// 保存项目为库项目并存储到仓库中
Store(cstore::CommandsStore),
/// 在当前项目下添加库依赖
Add(cadd::CommandsAdd),
/// 运行单元测试
Test(ctest::CommandsTest),
}
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_options,
run,
mingw,
} => {
let mingw = if let Some(t) = mingw {
Some(PathBuf::from(t))
} else {
None
};
build_project::build_project(build_options, run, mingw)?;
}
}
Commands::New(config) => cnew::run(config),
Commands::Build(config) => cbuild::run(config),
Commands::Store(config) => cstore::run(config),
Commands::Add(config) => cadd::run(config),
Commands::Test(config) => ctest::run(config),
}?;
Ok(())
}

View File

@ -1,23 +0,0 @@
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);
}

View File

@ -1,14 +0,0 @@
// 返回生成文件的文本
pub fn get_readme_text() -> &'static str {
""
}
pub fn get_maincpp_text() -> &'static str {
r##"#include <iostream>
int main() {
std::cout << "Hello World!" << std::endl;
}
"##
}

29
src/rtools/config.rs Normal file
View File

@ -0,0 +1,29 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct RConfig {
mingw_path: Option<String>,
}
impl RConfig {
/// 从config.json中读取配置文件
pub fn read() -> Self {
let path = super::get_home_path().join("config.json");
let json = std::fs::read(path).unwrap();
let json = String::from_utf8(json).unwrap();
let ans = serde_json::from_str(&json).unwrap();
return ans;
}
/// 从config.json中获取mingw文件夹名称并返回其bin目录
pub fn get_bin_path(&self) -> Option<PathBuf> {
if let Some(dir_name) = &self.mingw_path {
// 路径为: rust-cmaker/mingw_path/bin
let path: PathBuf = super::get_home_path().join(dir_name).join("bin");
return Some(path);
}
return None;
}
}

38
src/rtools/mod.rs Normal file
View File

@ -0,0 +1,38 @@
pub mod config;
use std::path::{Path, PathBuf};
/// 获取rust-cmaker文件夹目录
pub fn get_home_path() -> PathBuf {
let path = std::env::current_exe().unwrap();
let path = path.parent().unwrap().to_path_buf();
path.parent().unwrap().to_path_buf()
}
/// 复制文件夹到指定路径
pub fn copy_dir(src: &Path, dst: &Path) -> std::io::Result<()> {
if src.is_file() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"源路径必须是一个文件夹",
));
}
if !dst.exists() {
std::fs::create_dir_all(dst)?;
}
for entry in std::fs::read_dir(src)? {
let entry = entry?;
let path = entry.path();
let dst_path = dst.join(path.file_name().unwrap());
if entry.file_type()?.is_dir() {
copy_dir(&path, &dst_path)?;
} else {
std::fs::copy(&path, &&dst_path)?;
}
}
Ok(())
}