Compare commits
7 Commits
218a6306d4
...
working/#1
| Author | SHA1 | Date | |
|---|---|---|---|
| e25c27f082 | |||
| f30432c1db | |||
| 1b34dee81e | |||
| 46abec1237 | |||
| 49eaf6dbfc | |||
| 02009762c1 | |||
| e52ad0bc24 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -247,7 +247,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pallet"
|
name = "pallet"
|
||||||
version = "1.0.5"
|
version = "1.0.6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"clap_complete",
|
"clap_complete",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pallet"
|
name = "pallet"
|
||||||
version = "1.0.5"
|
version = "1.0.6"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "A project manager and build system for C inspired by Rust's Cargo"
|
description = "A project manager and build system for C inspired by Rust's Cargo"
|
||||||
|
|
||||||
|
|||||||
4
PKGBUILD
4
PKGBUILD
@@ -1,12 +1,12 @@
|
|||||||
# Maintainer: Shea Frembling <sfrembling@gmail.com>
|
# Maintainer: Shea Frembling <sfrembling@gmail.com>
|
||||||
pkgname=pallet
|
pkgname=pallet
|
||||||
pkgver=1.0.5
|
pkgver=1.0.6
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="A simple C project manager inspired by Cargo"
|
pkgdesc="A simple C project manager inspired by Cargo"
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url=""
|
url=""
|
||||||
license=('MIT')
|
license=('MIT')
|
||||||
depends=()
|
depends=('gcc')
|
||||||
makedepends=('rust' 'cargo')
|
makedepends=('rust' 'cargo')
|
||||||
source=()
|
source=()
|
||||||
|
|
||||||
|
|||||||
24
README.md
24
README.md
@@ -6,11 +6,29 @@ This is a toy project not meant to be taken very seriously.
|
|||||||
|
|
||||||
## Requirements for Use
|
## Requirements for Use
|
||||||
|
|
||||||
[GCC](https://en.wikipedia.org/wiki/GNU_Compiler_Collection) is required as it is currently the back-end tool used to compile the C code.
|
[GCC](https://en.wikipedia.org/wiki/GNU_Compiler_Collection) is recommended as the compiler installed for `Pallet` projects.
|
||||||
|
|
||||||
This tool calls `gcc` internally, so without it, it would fail.
|
You can define a different `gcc` compatible compiler instead per-project by editing `Pallet.toml`:
|
||||||
|
|
||||||
At some point I would like to update `Pallet.toml` to instead allow more configuring of how the build should be performed.
|
```toml
|
||||||
|
name = "my-app"
|
||||||
|
default_build = "debug"
|
||||||
|
compiler = "clang"
|
||||||
|
|
||||||
|
[[build]]
|
||||||
|
name = "debug"
|
||||||
|
args = [
|
||||||
|
"-g",
|
||||||
|
"-O0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[build]]
|
||||||
|
name = "release"
|
||||||
|
args = [
|
||||||
|
"-DNDEBUG",
|
||||||
|
"-O3",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
92
src/app.rs
92
src/app.rs
@@ -9,6 +9,7 @@ use colored::Colorize;
|
|||||||
use glob::glob;
|
use glob::glob;
|
||||||
|
|
||||||
const MAIN_C: &str = include_str!("templates/main.c");
|
const MAIN_C: &str = include_str!("templates/main.c");
|
||||||
|
const TEST_C: &str = include_str!("templates/it_works.c");
|
||||||
const GITIGNORE: &str = "target/";
|
const GITIGNORE: &str = "target/";
|
||||||
|
|
||||||
#[derive(clap::Parser)]
|
#[derive(clap::Parser)]
|
||||||
@@ -29,6 +30,9 @@ enum Subcommand {
|
|||||||
Init,
|
Init,
|
||||||
/// Run the local project
|
/// Run the local project
|
||||||
Run {
|
Run {
|
||||||
|
/// Force recompilation of the project
|
||||||
|
#[arg(long, short)]
|
||||||
|
force_recompile: bool,
|
||||||
/// The build mode to use
|
/// The build mode to use
|
||||||
mode: Option<String>,
|
mode: Option<String>,
|
||||||
/// Arguments to pass to the project binary
|
/// Arguments to pass to the project binary
|
||||||
@@ -37,6 +41,9 @@ enum Subcommand {
|
|||||||
},
|
},
|
||||||
/// Build the local project
|
/// Build the local project
|
||||||
Build {
|
Build {
|
||||||
|
/// Force recompilation of the project
|
||||||
|
#[arg(long, short)]
|
||||||
|
force_recompile: bool,
|
||||||
/// The build mode to use
|
/// The build mode to use
|
||||||
mode: Option<String>,
|
mode: Option<String>,
|
||||||
},
|
},
|
||||||
@@ -47,6 +54,11 @@ enum Subcommand {
|
|||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
command: UtilSubcommand,
|
command: UtilSubcommand,
|
||||||
},
|
},
|
||||||
|
/// Run tests in your project
|
||||||
|
Test {
|
||||||
|
/// The build mode to use
|
||||||
|
mode: Option<String>,
|
||||||
|
},
|
||||||
/// List available build modes
|
/// List available build modes
|
||||||
List,
|
List,
|
||||||
}
|
}
|
||||||
@@ -72,47 +84,47 @@ enum ShellCompletions {
|
|||||||
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 } => {
|
||||||
Err(e) => {
|
if let Err(e) = create_project(&name) {
|
||||||
eprintln!("Error creating project '{name}': {e}");
|
eprintln!("Error creating project '{name}': {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
Subcommand::Init => {
|
||||||
Subcommand::Init => match create_project(".") {
|
if let Err(e) = create_project(".") {
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error initializing project: {e}");
|
eprintln!("Error initializing project: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
Subcommand::Run {
|
||||||
Subcommand::Run { mode, args } => {
|
mode,
|
||||||
match build(&mode) {
|
args,
|
||||||
Err(e) => {
|
force_recompile,
|
||||||
|
} => {
|
||||||
|
if let Err(e) = build(&mode, force_recompile) {
|
||||||
eprintln!("Error building project: {e}");
|
eprintln!("Error building project: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
};
|
||||||
if let Err(e) = run(&mode, args) {
|
if let Err(e) = run(&mode, args) {
|
||||||
eprintln!("Error running project: {e}");
|
eprintln!("Error running project: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Subcommand::Build { mode } => match build(&mode) {
|
Subcommand::Build {
|
||||||
Err(e) => {
|
mode,
|
||||||
|
force_recompile,
|
||||||
|
} => {
|
||||||
|
if let Err(e) = build(&mode, force_recompile) {
|
||||||
eprintln!("Error building project: {e}");
|
eprintln!("Error building project: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
Subcommand::Clean => {
|
||||||
Subcommand::Clean => match clean() {
|
if let Err(e) = clean() {
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Error cleaning project: {e}");
|
eprintln!("Error cleaning project: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
|
||||||
Subcommand::Utils { command } => match command {
|
Subcommand::Utils { command } => match command {
|
||||||
UtilSubcommand::Completions { shell } => {
|
UtilSubcommand::Completions { shell } => {
|
||||||
let name = env!("CARGO_PKG_NAME");
|
let name = env!("CARGO_PKG_NAME");
|
||||||
@@ -145,15 +157,28 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Subcommand::List => match list() {
|
Subcommand::List => {
|
||||||
Err(e) => {
|
if let Err(e) = list() {
|
||||||
eprintln!("Error listing build profiles: {e}");
|
eprintln!("Error listing build profiles: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
}
|
||||||
},
|
Subcommand::Test { mode } => {
|
||||||
|
if let Err(e) = test(mode) {
|
||||||
|
eprintln!("Error running tests: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test(mode: Option<String>) -> std::io::Result<()> {
|
||||||
|
for entry in glob("src/tests/*.c").expect("a valid glob pattern") {
|
||||||
|
if let Ok(file) = entry {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list() -> std::io::Result<()> {
|
fn list() -> std::io::Result<()> {
|
||||||
@@ -220,7 +245,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>, force_recompile: bool) -> std::io::Result<()> {
|
||||||
let conf = match get_config() {
|
let conf = match get_config() {
|
||||||
Some(conf) => conf,
|
Some(conf) => conf,
|
||||||
None => {
|
None => {
|
||||||
@@ -249,7 +274,7 @@ fn build(mode: &Option<String>) -> std::io::Result<()> {
|
|||||||
|
|
||||||
let old_compute_path = PathBuf::from(format!("target/{}/.build_hash", build_config.name));
|
let old_compute_path = PathBuf::from(format!("target/{}/.build_hash", build_config.name));
|
||||||
|
|
||||||
if old_compute_path.exists() {
|
if old_compute_path.exists() && !force_recompile {
|
||||||
let text = std::fs::read_to_string(old_compute_path)?;
|
let text = std::fs::read_to_string(old_compute_path)?;
|
||||||
|
|
||||||
if hash.trim() == text.trim() {
|
if hash.trim() == text.trim() {
|
||||||
@@ -261,7 +286,7 @@ fn build(mode: &Option<String>) -> std::io::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut command = Command::new("gcc");
|
let mut command = Command::new(conf.compiler.as_deref().unwrap_or("gcc"));
|
||||||
|
|
||||||
for arg in &build_config.args {
|
for arg in &build_config.args {
|
||||||
command.arg(arg);
|
command.arg(arg);
|
||||||
@@ -279,9 +304,14 @@ fn build(mode: &Option<String>) -> std::io::Result<()> {
|
|||||||
.arg("-o")
|
.arg("-o")
|
||||||
.arg(format!("target/{}/{}", build_config.name, conf.name));
|
.arg(format!("target/{}/{}", build_config.name, conf.name));
|
||||||
|
|
||||||
let mut child = command.spawn()?;
|
let status = command.status()?;
|
||||||
|
|
||||||
child.wait()?;
|
if !status.success() {
|
||||||
|
return Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::Other,
|
||||||
|
format!("compiler exited with status {status}"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
std::fs::write(format!("target/{}/.build_hash", build_config.name), hash)?;
|
std::fs::write(format!("target/{}/.build_hash", build_config.name), hash)?;
|
||||||
|
|
||||||
@@ -347,7 +377,7 @@ fn run(mode: &Option<String>, args: Option<Vec<String>>) -> std::io::Result<()>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn clean() -> std::io::Result<()> {
|
fn clean() -> std::io::Result<()> {
|
||||||
if let None = get_config() {
|
if get_config().is_none() {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::NotFound,
|
std::io::ErrorKind::NotFound,
|
||||||
"no Pallet.toml found in local dir",
|
"no Pallet.toml found in local dir",
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#[derive(serde::Deserialize, serde::Serialize, Default)]
|
#[derive(serde::Deserialize, serde::Serialize, Default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
/// The C compiler to use (defaults to "gcc")
|
||||||
|
pub compiler: Option<String>,
|
||||||
/// 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
|
||||||
|
|||||||
7
src/templates/it_works.c
Normal file
7
src/templates/it_works.c
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
assert((1 + 1) == 2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user