Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add run command to CLI #106

Merged
merged 14 commits into from
Apr 17, 2024
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ target/
.vscode/
lcov.info

build_artifacts/
build/

*.so
*.a
284 changes: 175 additions & 109 deletions crates/concrete_driver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use config::{Package, Profile};
use git2::{IndexAddOption, Repository};
use owo_colors::OwoColorize;
use std::io::Read;
use std::os::unix::process::CommandExt;
use std::{collections::HashMap, fs::File, path::PathBuf, time::Instant};
use walkdir::WalkDir;

Expand Down Expand Up @@ -55,6 +56,19 @@ enum Commands {
#[arg(short, long, default_value_t = false)]
release: bool,

/// Override the profile to use.
#[arg(short, long)]
profile: Option<String>,
},
/// Run a concrete file
Run {
#[arg(required = false)]
path: Option<PathBuf>,

/// Build for release with all optimizations.
#[arg(short, long, default_value_t = false)]
release: bool,

/// Override the profile to use.
#[arg(short, long)]
profile: Option<String>,
Expand Down Expand Up @@ -125,7 +139,7 @@ pub fn main() -> Result<()> {
} => {
let name = name.unwrap_or_else(|| {
path.file_name()
.context("Failed to get project name")
.context("failed to get project name")
.unwrap()
.to_string_lossy()
.to_string()
Expand Down Expand Up @@ -234,126 +248,178 @@ mod {} {{
}
}
Commands::Build { release, profile } => {
let mut current_dir = std::env::current_dir()?;
let mut config_path = None;

// Go up to 3 parent folders to find Concrete.toml
for _ in 0..3 {
if !current_dir.join("Concrete.toml").exists() {
current_dir = if let Some(parent) = current_dir.parent() {
parent.to_path_buf()
} else {
bail!("Couldn't find Concrete.toml");
build_command(profile, release)?;
}
Commands::Run {
path,
release,
profile,
} => {
let output = match path {
Some(input) => {
let input_stem = input
.file_stem()
.context("could not get file stem")?
.to_str()
.context("could not convert file stem to string")?;

let build_dir = std::env::current_dir()?.join("build");
if !build_dir.exists() {
std::fs::create_dir_all(&build_dir)?;
}
let output = build_dir.join(input_stem);

let compile_args = CompilerArgs {
input: input.clone(),
output: output.clone(),
release,
optlevel: None,
debug_info: None,
library: false,
ast: false,
ir: false,
llvm: true,
asm: false,
object: true,
mlir: true,
};
} else {
config_path = Some(current_dir.join("Concrete.toml"));
break;
}
}

let config_path = match config_path {
Some(x) => x,
None => bail!("Couldn't find Concrete.toml"),
println!(
" {} {} ({})",
"Compiling".green().bold(),
input_stem,
input.display()
);

let start = Instant::now();
let object = compile(&compile_args)?;
link_binary(&[object], &output)?;
let elapsed = start.elapsed();

println!(
" {} {} in {elapsed:?}",
"Finished".green().bold(),
if release { "release" } else { "dev" },
);

output
}
None => build_command(profile, release)?,
};

let base_dir = config_path
.parent()
.context("couldn't get config parent dir")?;
let mut config = File::open(&config_path).context("Failed to open Concrete.toml")?;
let mut buf = String::new();
config.read_to_string(&mut buf)?;

let config: Config = toml::from_str(&buf).context("failed to parse Concrete.toml")?;

println!(
" {} {} v{} ({})",
"Compiling".green().bold(),
config.package.name,
config.package.version,
base_dir.display()
);
println!(" {} {}", "Running".green().bold(), &output.display());

let src_dir = base_dir.join("src");
let target_dir = base_dir.join("build");
Err(std::process::Command::new(output).exec())?;
}
}

if !target_dir.exists() {
std::fs::create_dir_all(&target_dir)?;
}
Ok(())
}

let has_main = src_dir.join("main.con").exists();
let output = target_dir.join(config.package.name);

let (profile, profile_name) = if let Some(profile) = profile {
(
config
.profile
.get(&profile)
.context("Couldn't get requested profile")?,
profile,
)
} else if release {
(
config
.profile
.get("release")
.context("Couldn't get profile: release")?,
"release".to_string(),
)
fn build_command(profile: Option<String>, release: bool) -> Result<PathBuf> {
let mut current_dir = std::env::current_dir()?;
let mut config_path = None;
for _ in 0..3 {
if !current_dir.join("Concrete.toml").exists() {
current_dir = if let Some(parent) = current_dir.parent() {
parent.to_path_buf()
} else {
(
config
.profile
.get("dev")
.context("Couldn't get profile: dev")?,
"dev".to_string(),
)
bail!("couldn't find Concrete.toml");
};

let compile_args = CompilerArgs {
input: src_dir,
output: output.clone(),
release,
optlevel: Some(profile.opt_level),
debug_info: Some(profile.debug_info),
library: !has_main,
ast: false,
ir: false,
llvm: true,
asm: false,
object: true,
mlir: true,
};

let start = Instant::now();
let object = compile(&compile_args)?;

if !has_main {
link_shared_lib(&[object], &output)?;
} else {
link_binary(&[object], &output)?;
}

let elapsed = start.elapsed();

println!(
" {} {} [{}{}] in {elapsed:?}",
"Finished".green().bold(),
profile_name,
if profile.opt_level > 0 {
"optimized"
} else {
"unoptimized"
},
if profile.debug_info {
" + debuginfo"
} else {
""
}
);
} else {
config_path = Some(current_dir.join("Concrete.toml"));
break;
}
}
let config_path = match config_path {
Some(x) => x,
None => bail!("couldn't find Concrete.toml"),
};
let base_dir = config_path
.parent()
.context("couldn't get config parent dir")?;
let mut config = File::open(&config_path).context("failed to open Concrete.toml")?;
let mut buf = String::new();
config.read_to_string(&mut buf)?;
let config: Config = toml::from_str(&buf).context("failed to parse Concrete.toml")?;
JulianGCalderon marked this conversation as resolved.
Show resolved Hide resolved
println!(
" {} {} v{} ({})",
"Compiling".green().bold(),
config.package.name,
config.package.version,
base_dir.display()
);
let src_dir = base_dir.join("src");
let target_dir = base_dir.join("build");
if !target_dir.exists() {
std::fs::create_dir_all(&target_dir)?;
}
let has_main = src_dir.join("main.con").exists();
let output = target_dir.join(config.package.name);
let (profile, profile_name) = if let Some(profile) = profile {
(
config
.profile
.get(&profile)
.context("couldn't get requested profile")?,
profile,
)
} else if release {
(
config
.profile
.get("release")
.context("couldn't get profile: release")?,
"release".to_string(),
)
} else {
(
config
.profile
.get("dev")
.context("couldn't get profile: dev")?,
"dev".to_string(),
)
};
let compile_args = CompilerArgs {
input: src_dir,
output: output.clone(),
release,
optlevel: Some(profile.opt_level),
debug_info: Some(profile.debug_info),
library: !has_main,
ast: false,
ir: false,
llvm: true,
asm: false,
object: true,
mlir: true,
};
let start = Instant::now();
let object = compile(&compile_args)?;
if !has_main {
link_shared_lib(&[object], &output)?;
} else {
link_binary(&[object], &output)?;
}
let elapsed = start.elapsed();
println!(
" {} {} [{}{}] in {elapsed:?}",
"Finished".green().bold(),
profile_name,
if profile.opt_level > 0 {
"optimized"
} else {
"unoptimized"
},
if profile.debug_info {
" + debuginfo"
} else {
""
}
);

Ok(())
Ok(output)
}

pub fn compile(args: &CompilerArgs) -> Result<PathBuf> {
Expand Down
14 changes: 14 additions & 0 deletions examples/project/Concrete.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "project"
version = "0.1.0"
license = "MIT"

[profile.release]
release = true
opt_level = 3
debug_info = false

[profile.dev]
release = false
opt_level = 0
debug_info = true
6 changes: 6 additions & 0 deletions examples/project/src/main.con
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

mod project {
pub fn main() -> i32 {
return 17;
}
}
Loading