Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 198 additions & 119 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ members = [

[workspace.dependencies.cu]
package = "pistonite-cu"
version = "0.5.2"
version = "0.6.0"
# path = "../../../cu/packages/copper"
4 changes: 3 additions & 1 deletion packages/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ publish = false

[dependencies.cu]
workspace = true
features = ["cli", "process", "prompt", "toml"]
features = ["cli", "process", "prompt", "toml", "json", "fs"]

[dependencies]
serde = "1"
flate2 = "1.1.2"
tar = "0.4.44"
derive_more = { version = "2.0.1", features = ["as_ref", "deref", "deref_mut"] }
fxhash = "0.2.1"
depfile = "0.1.1"

[build-dependencies.cu]
workspace = true
Expand Down
216 changes: 199 additions & 17 deletions packages/cli/src/cmds/cmd_build/compile.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,210 @@
// This modules handles compiling c/c++/asm/rust code
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Megaton contributors

use cu::Result;
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
};

use super::{Flags, Lang, RustCrate, SourceFile};
use cu::pre::*;
use fxhash::hash;

// Compiles the given source file and writes it to `out`
pub fn compile(src: &SourceFile, out: &str, flags: Flags) -> Result<()> {
// TODO: Implement
use super::{Flags, RustCrate};
use crate::{cmds::cmd_build::BuildEnvironment, env::environment};

// match src.lang {
// Lang::C => todo!(),
// Lang::Cxx => todo!(),
// Lang::Asm => todo!(),
// };
#[derive(Serialize, Deserialize)]
pub struct CompileDB {
commands: BTreeMap<String, CompileRecord>,
cc_version: String,
cxx_version: String,
}

Ok(todo!())
impl CompileDB {
// Creates a new compile record and adds it to the db
fn update(&mut self, command: CompileCommand) -> cu::Result<()> {
todo!()
}
}

#[derive(Serialize, Deserialize)]
struct CompileRecord {
command: CompileCommand,
}

#[derive(Serialize, Deserialize, PartialEq, Eq)]
struct CompileCommand {
compiler: PathBuf,
source: PathBuf,
args: Vec<String>,
}

impl CompileCommand {
fn new(
compiler_path: &Path,
src_file: &Path,
out_file: &Path,
flags: &Vec<String>,
) -> cu::Result<Self> {
let mut argv = flags.clone();
argv.push(
src_file
.as_utf8()
.context("failed to parse utf-8")?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to add context here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same below

.to_string(),
);
argv.push(String::from("-c"));
argv.push(format!(
"-o{}",
out_file.as_utf8().context("failed to parse utf-8")?
));

Ok(Self {
compiler: compiler_path.to_path_buf(),
source: src_file.to_path_buf(),
args: argv,
})
}

fn execute(&self) -> cu::Result<()> {
// TODO: Build and execute a cu::command
todo!()
}

// We need two different ways of serializing this data since it will
// need to be writen to the compiledb cache and the compile_commands.json
// and the format will be different for each
fn to_clangd_json(&self) -> String {
// TODO: Implement
todo!()
}
}

// A source file and its corresponding artifacts
pub struct SourceFile {
lang: Lang,
path: PathBuf,
basename: String,
hash: usize,
}

impl SourceFile {
pub fn new(lang: Lang, path: PathBuf) -> cu::Result<Self> {
let basename = cu::PathExtension::file_name_str(&path)
.context("path is not utf-8")?
.to_owned();
let hash = hash(&cu::fs::read(&path).context("Failed to read source file")?);
Ok(Self {
lang,
path,
basename,
hash,
})
}

pub fn compile(
&self,
flags: &Flags,
build_env: &BuildEnvironment,
compile_db: &mut CompileDB,
) -> cu::Result<()> {
let o_path = PathBuf::from(format!(
"{}/{}/{}/o/{}-{}.o",
build_env.target, build_env.profile, build_env.module, self.basename, self.hash
));
let d_path = PathBuf::from(format!(
"{}/{}/{}/o/{}-{}.d",
build_env.target, build_env.profile, build_env.module, self.basename, self.hash
));

let (comp_path, comp_flags) = match self.lang {
Lang::C => (environment().cc_path(), &flags.cflags),
Lang::Cpp => (environment().cxx_path(), &flags.cxxflags),
Lang::S => (environment().cc_path(), &flags.sflags),
};

let comp_command = CompileCommand::new(comp_path, &self.path, &o_path, &comp_flags)?;

if self.need_recompile(compile_db, build_env, &o_path, &d_path, &comp_command)? {
// Compile and update record
comp_command.execute()?;

// Ensure source and artifacts have the same timestamp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should read the timestamp of the source before compiling and set it on the artifacts afterwards.

What if the source changes while it's being compiled? the current implementation would not recompile

let now = cu::fs::Time::now();
cu::fs::set_mtime(o_path, now)?;
cu::fs::set_mtime(d_path, now)?;
cu::fs::set_mtime(&self.path, now)?;

compile_db.update(comp_command)?;
}

Ok(())
}

fn need_recompile(
&self,
compile_db: &CompileDB,
build_env: &BuildEnvironment,
o_path: &Path,
d_path: &Path,
command: &CompileCommand,
) -> cu::Result<bool> {
// Check if record exists
let comp_record = match compile_db.commands.get(&self.basename) {
Some(record) => record,
None => return Ok(true),
};

// Check if artifacts exist
if !o_path.exists() || !d_path.exists() {
return Ok(true);
}

// Check if artifacts are up to date
if cu::fs::get_mtime(o_path)? != cu::fs::get_mtime(&self.path)?
|| cu::fs::get_mtime(d_path)? != cu::fs::get_mtime(&self.path)?
{
return Ok(true);
}

let d_file_contents = cu::fs::read_string(d_path)?;
let depfile = match depfile::parse(&&d_file_contents) {
Ok(depfile) => depfile,

// Make sure our errors are all cu compatible
Err(_) => return Err(cu::Error::msg("Failed to parse depfile")),
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

depfile::parse(...).context("...")?;

or

cu::check!(depfile::parse(...), ...format args)?;


for dep in depfile.recurse_deps(o_path.as_utf8()?) {
if cu::fs::get_mtime(PathBuf::from(dep))? != cu::fs::get_mtime(&self.path)? {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. no need to PathBuf::from
  2. self.path mtime can be put outside of the loop

return Ok(true);
}
}

if command != &comp_record.command {
return Ok(true);
}

if (self.lang == Lang::Cpp && compile_db.cxx_version != build_env.cxx_version)
|| compile_db.cc_version != build_env.cc_version
{
return Ok(true);
}

// No need to recompile!
Ok(false)
}
}

// Specifies source language (rust is managed separately)
#[derive(PartialEq, Eq)]
pub enum Lang {
C,
Cpp,
S,
}

// Builds the give rust crate and places the binary in the target as specified in the rust manifest
pub fn compile_rust(rust_crate: RustCrate) -> Result<()> {
pub fn compile_rust(rust_crate: RustCrate) -> cu::Result<()> {
// TODO: Implement
Ok(todo!())
}

fn check_needs_recompile() -> bool {
false
}
14 changes: 12 additions & 2 deletions packages/cli/src/cmds/cmd_build/config/main_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,13 @@ pub struct Module {
pub title_id: u64,

/// The target directory to put build files
pub target: Option<String>,
#[serde(default = "default_target")]
pub target: String,

/// The compile_commands.json file to put/update compile commands
/// for other tools like clangd
pub compdb: Option<String>,
#[serde(default = "default_comp_commands")]
pub compdb: String,

#[serde(flatten, default)]
unused: CaptureUnused,
Expand Down Expand Up @@ -202,6 +204,14 @@ impl Module {
}
}

fn default_target() -> String {
"target".to_string()
}

fn default_comp_commands() -> String {
"compile_commands.json".to_string()
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProfileConfig {
/// Set the profile to use when profile is "none"
Expand Down
7 changes: 5 additions & 2 deletions packages/cli/src/cmds/cmd_build/generate.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Megaton contributors

use std::path::Path;

use cu::Result;
use cu::pre::*;

use super::RustCrate;


pub fn generate_cxx_bridge_src(rust_crate: RustCrate, module_target_path: impl AsRef<Path>) -> Result<()> {
pub fn generate_cxx_bridge_src(rust_crate: RustCrate, module_target_path: impl AsRef<Path>) -> cu::Result<()> {
// TODO: Parse rust crate for cxxbridge files
//
// TODO: Place generated headers in {module}/include/rust/
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/cmds/cmd_build/link.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// This module manages linking the mod and library
// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Megaton contributors

use cu::Result;
use cu::pre::*;

// Link build artifacts into a shared object elf
pub fn link() -> Result<()> {
pub fn link() -> cu::Result<()> {
Ok(todo!())
}

// Convert the linked mod into an nso
pub fn make_nso() -> Result<()> {
pub fn make_nso() -> cu::Result<()> {
Ok(todo!())
}
Loading
Loading