Compare commits
9 Commits
b9b35f3f0f
...
v1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 528bc1be87 | |||
| 40ce49a180 | |||
| 15ff177b18 | |||
| 9a2ce7b1cc | |||
| f8e2dbfd5c | |||
| b999b67ed8 | |||
| 84a6196416 | |||
| e7b8596205 | |||
| 40685b7c4d |
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -98,6 +98,15 @@ version = "1.0.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "3.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -146,9 +155,10 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallet"
|
name = "pallet"
|
||||||
version = "0.1.0"
|
version = "1.0.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"colored",
|
||||||
"glob",
|
"glob",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pallet"
|
name = "pallet"
|
||||||
version = "0.1.0"
|
version = "1.0.2"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.6.0", features = ["derive"] }
|
clap = { version = "4.6.0", features = ["derive"] }
|
||||||
|
colored = "3.1.1"
|
||||||
glob = "0.3.3"
|
glob = "0.3.3"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
toml = "1.0.7"
|
toml = "1.0.7"
|
||||||
|
|||||||
32
README.md
32
README.md
@@ -1,6 +1,6 @@
|
|||||||
# Pallet
|
# Pallet
|
||||||
|
|
||||||
Pallet is a project manager and build system for C inspired by Cargo for Rust.
|
Pallet is a project manager and build system for C inspired by Rust's Cargo.
|
||||||
|
|
||||||
This is a toy project not meant to be taken very seriously.
|
This is a toy project not meant to be taken very seriously.
|
||||||
|
|
||||||
@@ -10,6 +10,7 @@ This is a toy project not meant to be taken very seriously.
|
|||||||
- `pallet init`: initializes a new project in the current directory
|
- `pallet init`: initializes a new project in the current directory
|
||||||
- `pallet run`: runs the local project
|
- `pallet run`: runs the local project
|
||||||
- `pallet build`: builds the local project
|
- `pallet build`: builds the local project
|
||||||
|
- `pallet clean`: cleans the local project's build artifacts
|
||||||
|
|
||||||
## Configuring
|
## Configuring
|
||||||
|
|
||||||
@@ -17,4 +18,31 @@ You can configure options by editing `Pallet.toml`
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
TBD
|
- `name`: the name of the output executable
|
||||||
|
- `default_build`: the name of the default build profile to use
|
||||||
|
|
||||||
|
Additionally, one can define one or more build profiles with the following parameters:
|
||||||
|
|
||||||
|
- `name`: the name of the build profile
|
||||||
|
- `args`: the args to supply to gcc when using this profile
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
name = "my-app"
|
||||||
|
default_build = "debug"
|
||||||
|
|
||||||
|
[[build]]
|
||||||
|
name = "debug"
|
||||||
|
args = [
|
||||||
|
"-g",
|
||||||
|
"-O0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[build]]
|
||||||
|
name = "release"
|
||||||
|
args = [
|
||||||
|
"-DNDEBUG",
|
||||||
|
"-O3",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|||||||
158
src/app.rs
158
src/app.rs
@@ -1,11 +1,17 @@
|
|||||||
use std::{
|
use std::{
|
||||||
env::set_current_dir,
|
env::set_current_dir,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use colored::Colorize;
|
||||||
|
use glob::glob;
|
||||||
|
|
||||||
const MAIN_C: &str = include_str!("templates/main.c");
|
const MAIN_C: &str = include_str!("templates/main.c");
|
||||||
|
const GITIGNORE: &str = "target/";
|
||||||
|
|
||||||
#[derive(clap::Parser)]
|
#[derive(clap::Parser)]
|
||||||
|
#[clap(version)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
command: Subcommand,
|
command: Subcommand,
|
||||||
@@ -24,38 +30,80 @@ enum Subcommand {
|
|||||||
Run {
|
Run {
|
||||||
/// The build mode to use
|
/// The build mode to use
|
||||||
mode: Option<String>,
|
mode: Option<String>,
|
||||||
|
/// Arguments to pass to the project binary
|
||||||
|
#[arg(long, short)]
|
||||||
|
args: Option<Vec<String>>,
|
||||||
},
|
},
|
||||||
/// Build the local project
|
/// Build the local project
|
||||||
Build {
|
Build {
|
||||||
/// The build mode to use
|
/// The build mode to use
|
||||||
mode: Option<String>,
|
mode: Option<String>,
|
||||||
},
|
},
|
||||||
|
/// Clean all in progress files
|
||||||
|
Clean,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn run(self) {
|
pub fn run(self) {
|
||||||
match self.command {
|
match self.command {
|
||||||
Subcommand::New { name } => match create_project(&name) {
|
Subcommand::New { name } => match create_project(&name) {
|
||||||
Ok(_) => println!("Successfully created new project '{name}'."),
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error creating project '{name}': {e}");
|
eprintln!("Error creating project '{name}': {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
},
|
},
|
||||||
Subcommand::Init => match create_project(".") {
|
Subcommand::Init => match create_project(".") {
|
||||||
Ok(_) => println!("Successfully initialized new project."),
|
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error initializing project: {e}");
|
eprintln!("Error initializing project: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Subcommand::Run { mode, args } => {
|
||||||
|
match build(&mode) {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error building project: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
if let Err(e) = run(&mode, args) {
|
||||||
|
eprintln!("Error running project: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Subcommand::Build { mode } => match build(&mode) {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error building project: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Subcommand::Clean => match clean() {
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error cleaning project: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
},
|
},
|
||||||
Subcommand::Run { mode } => todo!(),
|
|
||||||
Subcommand::Build { mode } => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_project<P: AsRef<Path>>(directory: P) -> std::io::Result<()> {
|
fn create_project<P: AsRef<Path>>(directory: P) -> std::io::Result<()> {
|
||||||
|
let name = if directory.as_ref().to_string_lossy() == "." {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
format!(" '{}'", directory.as_ref().to_string_lossy())
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(
|
||||||
|
" {} binary (application){}",
|
||||||
|
"Creating".green().bold(),
|
||||||
|
name
|
||||||
|
);
|
||||||
|
|
||||||
let pathdir = directory.as_ref();
|
let pathdir = directory.as_ref();
|
||||||
|
|
||||||
if pathdir.exists() && pathdir.to_string_lossy() != "." {
|
if pathdir.exists() && pathdir.to_string_lossy() != "." {
|
||||||
@@ -74,6 +122,7 @@ fn create_project<P: AsRef<Path>>(directory: P) -> std::io::Result<()> {
|
|||||||
std::fs::create_dir("src")?;
|
std::fs::create_dir("src")?;
|
||||||
|
|
||||||
std::fs::write("src/main.c", MAIN_C)?;
|
std::fs::write("src/main.c", MAIN_C)?;
|
||||||
|
std::fs::write(".gitignore", GITIGNORE)?;
|
||||||
|
|
||||||
let config = crate::config::Config::new(&pathdir.to_string_lossy());
|
let config = crate::config::Config::new(&pathdir.to_string_lossy());
|
||||||
|
|
||||||
@@ -94,7 +143,7 @@ fn get_config() -> Option<crate::config::Config> {
|
|||||||
toml::from_str(&raw).ok()
|
toml::from_str(&raw).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(mode: Option<String>) -> std::io::Result<()> {
|
fn build(mode: &Option<String>) -> std::io::Result<()> {
|
||||||
let conf = match get_config() {
|
let conf = match get_config() {
|
||||||
Some(conf) => conf,
|
Some(conf) => conf,
|
||||||
None => {
|
None => {
|
||||||
@@ -108,5 +157,102 @@ fn build(mode: Option<String>) -> std::io::Result<()> {
|
|||||||
"build layout not found",
|
"build layout not found",
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
todo!()
|
println!(
|
||||||
|
" {} '{}' profile for project '{}'",
|
||||||
|
"Building".green().bold(),
|
||||||
|
build_config.name,
|
||||||
|
conf.name
|
||||||
|
);
|
||||||
|
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
|
std::fs::create_dir_all(format!("target/{}", build_config.name))?;
|
||||||
|
|
||||||
|
let mut command = Command::new("gcc");
|
||||||
|
|
||||||
|
for arg in &build_config.args {
|
||||||
|
command.arg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry in glob("src/*.c")
|
||||||
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, format!("{e}").as_str()))?
|
||||||
|
{
|
||||||
|
if let Ok(path) = entry {
|
||||||
|
command.arg(path.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command
|
||||||
|
.arg("-o")
|
||||||
|
.arg(format!("target/{}/{}", build_config.name, conf.name));
|
||||||
|
|
||||||
|
let mut child = command.spawn()?;
|
||||||
|
|
||||||
|
child.wait()?;
|
||||||
|
|
||||||
|
let stop = start.elapsed();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
" {} '{}' profile for project '{}' in {:.2}s",
|
||||||
|
"Finished".green().bold(),
|
||||||
|
build_config.name,
|
||||||
|
conf.name,
|
||||||
|
stop.as_secs_f64()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(mode: &Option<String>, args: Option<Vec<String>>) -> std::io::Result<()> {
|
||||||
|
let conf = match get_config() {
|
||||||
|
Some(conf) => conf,
|
||||||
|
None => {
|
||||||
|
eprintln!("Error opening config file. No Pallet.toml in current directory.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let build_config = conf.get_or_default(mode).ok_or(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
"build layout not found",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
" {} '{}' profile for project '{}'",
|
||||||
|
"Running".green().bold(),
|
||||||
|
build_config.name,
|
||||||
|
conf.name
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut command = Command::new(format!("target/{}/{}", build_config.name, conf.name));
|
||||||
|
|
||||||
|
if let Some(args) = args {
|
||||||
|
for arg in args {
|
||||||
|
command.arg(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut child = command.spawn()?;
|
||||||
|
|
||||||
|
child.wait()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean() -> std::io::Result<()> {
|
||||||
|
if let None = get_config() {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
"no Pallet.toml found in local dir",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::remove_dir_all("target/")?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
" {} removed files in target/ directory",
|
||||||
|
"Successfully".green().bold()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[derive(serde::Deserialize, serde::Serialize, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// The name of the output binary
|
/// The name of the output binary
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// The default build to use
|
/// The default build to use
|
||||||
pub default_build: String,
|
pub default_build: String,
|
||||||
|
/// A brief description
|
||||||
|
pub description: Option<String>,
|
||||||
|
/// The version of the project
|
||||||
|
pub version: Option<String>,
|
||||||
|
/// The authors of the project
|
||||||
|
pub authors: Option<Vec<String>>,
|
||||||
/// Build configs
|
/// Build configs
|
||||||
pub build: Vec<BuildConf>,
|
pub build: Vec<BuildConf>,
|
||||||
}
|
}
|
||||||
@@ -14,12 +20,13 @@ impl Config {
|
|||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
default_build: "debug".to_owned(),
|
default_build: "debug".to_owned(),
|
||||||
build: vec![BuildConf::debug(), BuildConf::release()],
|
build: vec![BuildConf::debug(), BuildConf::release()],
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_or_default(&self, mode: Option<String>) -> Option<&BuildConf> {
|
pub fn get_or_default(&self, mode: &Option<String>) -> Option<&BuildConf> {
|
||||||
if let Some(mode) = mode {
|
if let Some(mode) = mode {
|
||||||
self.build.iter().find(|bc| bc.name == mode)
|
self.build.iter().find(|bc| bc.name == *mode)
|
||||||
} else {
|
} else {
|
||||||
self.build.iter().find(|bc| bc.name == self.default_build)
|
self.build.iter().find(|bc| bc.name == self.default_build)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user