大改了new和build指令
This commit is contained in:
parent
cf186539d9
commit
aad0727e8a
33
README.md
33
README.md
@ -4,27 +4,42 @@
|
||||
|
||||
> 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` , 之后我们就都用这个名字了
|
||||
1. 将本项目找个文件夹放好
|
||||
* 本项目需要一个`MinGW`并将其的`bin`目录放到你的环境变量里
|
||||
* 如果可能请直接将`MinGW`放到项目目录里,这样能省一点配置的时间
|
||||
2. 将本项目的bin目录加入到你的环境变量
|
||||
* 目前`bin`目录下应该只有一个名为`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`
|
||||
4. 进入生成的project文件夹, 用vscode打开它(`cd project`; `code .`)
|
||||
5. 在src目录下编写你的程序
|
||||
6. 使用`rcm run`构建程序并运行, 或者你需要`rcm build --release`进行一个o3优化
|
||||
7. 你可以显式指定`MinGW`文件夹的路径, 详情见`rcm build -h`,你或许可以用这个实现交叉编译
|
||||
8. 目前只能构建目录下`src/`文件夹里的 .c/.cpp 文件
|
||||
9. 默认统一使用`g++`指令来编译
|
||||
|
||||
#### 已知问题
|
||||
|
||||
1. 代码是赶出来的, 写的依托
|
||||
2. 没有链接库的功能
|
||||
3. 不能判断哪些文件是修改过的, 所以只能全部编译一遍, 很耗时, 而且不知道怎么修
|
||||
3. 不能判断哪些文件是修改过的, 所以只能全部编译一遍, 很耗时, 而且大概率以后也不会改
|
||||
4. 只能指定目录下`src/`文件夹里的内容来编译以及只能输出到`target/bin/`目录下, 之后或许会增加新的编译选项
|
||||
* 可能会影响到写学校的作业,但管他呢
|
||||
5. 需要手动配置环境变量
|
||||
6. 统一的用g++进行编译, 应该判断如果全是.c的文件就用gcc编译
|
||||
|
||||
#### 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. 推荐在`root/src`的每个文件夹(包括`src`文件夹它自己)下放一份`mod.h`文件, 引用这个`mod.h`文件就可以声明该目录下所有应包括的头文件, 包括子目录。这样构建的库项目会很清晰
|
||||
17
src/cbuild/command.rs
Normal file
17
src/cbuild/command.rs
Normal 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
125
src/cbuild/files.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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(path: PathBuf) -> Self {
|
||||
let mut entry_files = Vec::new();
|
||||
let mut c_files = Vec::new();
|
||||
let mut h_files = Vec::new();
|
||||
|
||||
/// 将路径文件夹内所有文件信息整理到集合中
|
||||
fn 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);
|
||||
read_dir(&new_path, c_files, h_files)?;
|
||||
} else {
|
||||
if let Some(name) = file.path().file_name() {
|
||||
let file_name: String = name.to_string_lossy().into_owned();
|
||||
let extension = std::path::Path::new(&file_name).extension().unwrap();
|
||||
let file_name: String = file
|
||||
.path()
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let extension: String = extension.to_string_lossy().into_owned();
|
||||
if extension == "c" || extension == "cpp" {
|
||||
c_files.push(File::new(file.path(), file_name, extension));
|
||||
} else if extension == "h" || extension == "hpp" {
|
||||
h_files.push(File::new(file.path(), file_name, extension));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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");
|
||||
read_dir(&new_path, &mut entry_files, &mut h_files).unwrap();
|
||||
} else {
|
||||
let new_path = path.join(name);
|
||||
read_dir(&new_path, &mut c_files, &mut h_files).unwrap();
|
||||
}
|
||||
} else {
|
||||
// 如果是文件
|
||||
if let Some(name) = file.path().file_name() {
|
||||
let file_name = name.to_string_lossy().into_owned();
|
||||
let file_path = file.path();
|
||||
let extension = std::path::Path::new(&file_name).extension().unwrap();
|
||||
let file_name: String = file
|
||||
.path()
|
||||
.file_stem()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let extension: String = extension.to_string_lossy().into_owned();
|
||||
let file = File::new(file_path, file_name, extension);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
entry_files,
|
||||
c_files,
|
||||
h_files,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,13 @@
|
||||
mod cmake_o;
|
||||
mod command;
|
||||
mod config;
|
||||
mod files;
|
||||
mod gcc;
|
||||
mod utils;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use crate::const_value::console_log;
|
||||
|
||||
use config::{BuildConfig, BuildConfigTrait};
|
||||
use self::files::FilesSet;
|
||||
|
||||
#[derive(clap::Parser, Debug)]
|
||||
pub struct CommandsBuild {
|
||||
@ -21,68 +23,116 @@ pub struct CommandsBuild {
|
||||
#[clap(short, long, default_value = "false")]
|
||||
run: bool,
|
||||
|
||||
/// MinGW编译器地址, 不填默认已配置为环境变量
|
||||
/// MinGW编译器地址, 不填则会从rcm的config文件中查找
|
||||
#[clap(short, long, default_value = None)]
|
||||
mingw: Option<String>,
|
||||
|
||||
/// 采用gcc编译指令而不是g++
|
||||
#[clap(short, default_value = "false")]
|
||||
c: bool,
|
||||
}
|
||||
|
||||
impl CommandsBuild {
|
||||
/// rcm run指令
|
||||
pub fn new_run() -> CommandsBuild {
|
||||
CommandsBuild {
|
||||
quiet: true,
|
||||
release: false,
|
||||
run: true,
|
||||
mingw: None,
|
||||
c: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(config: CommandsBuild) -> Result<(), std::io::Error> {
|
||||
// 获取项目路径
|
||||
let project_path = std::env::current_dir().unwrap();
|
||||
println!("{:?}", project_path);
|
||||
|
||||
// 获取配置
|
||||
let config: Box<dyn BuildConfigTrait> = Box::new(BuildConfig::new(
|
||||
config.mingw,
|
||||
String::from("main"),
|
||||
config.release,
|
||||
config.run,
|
||||
));
|
||||
// 获取指定的路径
|
||||
// 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目录存在
|
||||
|
||||
println!(
|
||||
"{}: start to compile 开始编译",
|
||||
crate::const_value::THIS_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())?; // 先删除目标文件
|
||||
if !config.quiet {
|
||||
console_log(&format!("项目路径{:?}", project_path));
|
||||
console_log(&format!("start to compile 开始编译"));
|
||||
}
|
||||
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())?;
|
||||
// 将src目录下的文件整理归纳
|
||||
let files: FilesSet = {
|
||||
let mut files = FilesSet::read(project_path.join("src"));
|
||||
// 如果src目录下仅有一个.c/.cpp文件, 则视为唯一项目入口
|
||||
if files.entry_files.len() == 0 && files.c_files.len() == 1 {
|
||||
let t = files.entry_files;
|
||||
files.entry_files = files.c_files;
|
||||
files.c_files = t;
|
||||
}
|
||||
files
|
||||
};
|
||||
|
||||
// build指令需要读取rust-cmaker下的config文件
|
||||
let r_config = crate::rtools::config::RConfig::read();
|
||||
let mut need_run = None;
|
||||
|
||||
// 将每一个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 c_file in &files.c_files {
|
||||
gcc_cmd.arg(&c_file.path);
|
||||
}
|
||||
|
||||
if !config.run {
|
||||
console_log(&format!("{:?}", gcc_cmd));
|
||||
}
|
||||
|
||||
let output = gcc_cmd.output();
|
||||
|
||||
match output {
|
||||
Ok(output) => {
|
||||
if !config.run {
|
||||
console_log(&format!("编译成功: {:?}", output));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if !config.run {
|
||||
console_log(&format!("执行出错: {}", err));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 构建完之后判断是否要立即运行
|
||||
if config.need_run() {
|
||||
println!("{}: 开始执行...", crate::const_value::THIS_PROJECT_NAME);
|
||||
let mut command = std::process::Command::new(config.getpath_target_file());
|
||||
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()?;
|
||||
println!("\n{}: 执行完毕...", crate::const_value::THIS_PROJECT_NAME);
|
||||
if !config.quiet {
|
||||
console_log(&format!("{}执行完毕: ", run_file.name));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -2,8 +2,6 @@ use clap::Parser;
|
||||
|
||||
use crate::{const_value::console_log, rtools};
|
||||
|
||||
mod text;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct CommandsNew {
|
||||
/// 项目名称
|
||||
|
||||
@ -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;
|
||||
}
|
||||
"##
|
||||
}
|
||||
@ -1,7 +1,5 @@
|
||||
use clap::Parser;
|
||||
|
||||
use crate::rtools;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct CommandsTest {
|
||||
/// 每完成一个测试暂停一次
|
||||
|
||||
@ -22,13 +22,13 @@ struct Args {
|
||||
enum Commands {
|
||||
/// 在当前目录新建项目
|
||||
New(cnew::CommandsNew),
|
||||
/// 构建当前打开的项目
|
||||
/// 构建当前目录下的项目
|
||||
Build(cbuild::CommandsBuild),
|
||||
/// `rcm build -q -r` 的简写
|
||||
Run,
|
||||
/// 生成静态库并存储到静态仓库
|
||||
/// 保存项目为库项目并存储到仓库中
|
||||
Store(cstore::CommandsStore),
|
||||
/// 在当前项目下添加静态库依赖
|
||||
/// 在当前项目下添加库依赖
|
||||
Add(cadd::CommandsAdd),
|
||||
/// 运行单元测试
|
||||
Test(ctest::CommandsTest),
|
||||
|
||||
29
src/rtools/config.rs
Normal file
29
src/rtools/config.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,5 @@
|
||||
pub mod config;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// 获取rust-cmaker文件夹目录
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user