9 Commits

Author SHA1 Message Date
528bc1be87 Update Cargo.lock 2026-03-22 18:59:34 -05:00
40ce49a180 Update vers 2026-03-22 18:57:53 -05:00
15ff177b18 update config to add some more useful fields 2026-03-22 18:57:28 -05:00
9a2ce7b1cc Remove gitingoretemplate since it was just the one line 2026-03-22 18:44:08 -05:00
f8e2dbfd5c Update README 2026-03-22 18:42:56 -05:00
b999b67ed8 fix --args flag not working as intended 2026-03-22 18:36:59 -05:00
84a6196416 finalize 2026-03-22 18:27:53 -05:00
e7b8596205 more dev work 2026-03-22 18:18:18 -05:00
40685b7c4d finish most work 2026-03-22 18:04:46 -05:00
5 changed files with 205 additions and 13 deletions

12
Cargo.lock generated
View File

@@ -98,6 +98,15 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "equivalent"
version = "1.0.2"
@@ -146,9 +155,10 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "pallet"
version = "0.1.0"
version = "1.0.2"
dependencies = [
"clap",
"colored",
"glob",
"serde",
"toml",

View File

@@ -1,10 +1,11 @@
[package]
name = "pallet"
version = "0.1.0"
version = "1.0.2"
edition = "2024"
[dependencies]
clap = { version = "4.6.0", features = ["derive"] }
colored = "3.1.1"
glob = "0.3.3"
serde = { version = "1.0.228", features = ["derive"] }
toml = "1.0.7"

View File

@@ -1,6 +1,6 @@
# 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.
@@ -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 run`: runs the local project
- `pallet build`: builds the local project
- `pallet clean`: cleans the local project's build artifacts
## Configuring
@@ -17,4 +18,31 @@ You can configure options by editing `Pallet.toml`
### 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",
]
```

View File

@@ -1,11 +1,17 @@
use std::{
env::set_current_dir,
path::{Path, PathBuf},
process::Command,
};
use colored::Colorize;
use glob::glob;
const MAIN_C: &str = include_str!("templates/main.c");
const GITIGNORE: &str = "target/";
#[derive(clap::Parser)]
#[clap(version)]
pub struct App {
#[clap(subcommand)]
command: Subcommand,
@@ -24,38 +30,80 @@ enum Subcommand {
Run {
/// The build mode to use
mode: Option<String>,
/// Arguments to pass to the project binary
#[arg(long, short)]
args: Option<Vec<String>>,
},
/// Build the local project
Build {
/// The build mode to use
mode: Option<String>,
},
/// Clean all in progress files
Clean,
}
impl App {
pub fn run(self) {
match self.command {
Subcommand::New { name } => match create_project(&name) {
Ok(_) => println!("Successfully created new project '{name}'."),
Err(e) => {
eprintln!("Error creating project '{name}': {e}");
std::process::exit(1);
}
_ => {}
},
Subcommand::Init => match create_project(".") {
Ok(_) => println!("Successfully initialized new project."),
Err(e) => {
eprintln!("Error initializing project: {e}");
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<()> {
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();
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::write("src/main.c", MAIN_C)?;
std::fs::write(".gitignore", GITIGNORE)?;
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()
}
fn build(mode: Option<String>) -> std::io::Result<()> {
fn build(mode: &Option<String>) -> std::io::Result<()> {
let conf = match get_config() {
Some(conf) => conf,
None => {
@@ -108,5 +157,102 @@ fn build(mode: Option<String>) -> std::io::Result<()> {
"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(())
}

View File

@@ -1,9 +1,15 @@
#[derive(serde::Deserialize, serde::Serialize)]
#[derive(serde::Deserialize, serde::Serialize, Default)]
pub struct Config {
/// The name of the output binary
pub name: String,
/// The default build to use
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
pub build: Vec<BuildConf>,
}
@@ -14,12 +20,13 @@ impl Config {
name: name.to_owned(),
default_build: "debug".to_owned(),
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 {
self.build.iter().find(|bc| bc.name == mode)
self.build.iter().find(|bc| bc.name == *mode)
} else {
self.build.iter().find(|bc| bc.name == self.default_build)
}