Compare commits
6 Commits
working/#1
...
99722302a2
| Author | SHA1 | Date | |
|---|---|---|---|
| 99722302a2 | |||
| a4b7a4d909 | |||
| 9c8fccbb4c | |||
| 29cbfbac66 | |||
| 4a2e959535 | |||
| 94d6f5d54d |
32
Cargo.lock
generated
32
Cargo.lock
generated
@@ -233,12 +233,24 @@ version = "1.70.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.183"
|
version = "0.2.183"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell_polyfill"
|
name = "once_cell_polyfill"
|
||||||
version = "1.70.2"
|
version = "1.70.2"
|
||||||
@@ -254,6 +266,7 @@ dependencies = [
|
|||||||
"colored",
|
"colored",
|
||||||
"glob",
|
"glob",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"sha256",
|
"sha256",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
@@ -312,6 +325,19 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.149"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -455,3 +481,9 @@ name = "winnow"
|
|||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
|
checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|||||||
@@ -10,5 +10,6 @@ clap_complete = "4.6.0"
|
|||||||
colored = "3.1.1"
|
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"] }
|
||||||
|
serde_json = "1.0.149"
|
||||||
sha256 = "1.6.0"
|
sha256 = "1.6.0"
|
||||||
toml = "1.0.7"
|
toml = "1.0.7"
|
||||||
|
|||||||
2
PKGBUILD
2
PKGBUILD
@@ -6,7 +6,7 @@ pkgdesc="A simple C project manager inspired by Cargo"
|
|||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
url=""
|
url=""
|
||||||
license=('MIT')
|
license=('MIT')
|
||||||
depends=('gcc')
|
depends=('gcc' 'pkgconf')
|
||||||
makedepends=('rust' 'cargo')
|
makedepends=('rust' 'cargo')
|
||||||
source=()
|
source=()
|
||||||
|
|
||||||
|
|||||||
292
src/app.rs
292
src/app.rs
@@ -9,8 +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/\ncompile_commands.json\n";
|
||||||
const GITIGNORE: &str = "target/";
|
|
||||||
|
|
||||||
#[derive(clap::Parser)]
|
#[derive(clap::Parser)]
|
||||||
#[clap(version, about)]
|
#[clap(version, about)]
|
||||||
@@ -54,13 +53,13 @@ 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,
|
||||||
|
/// Add a new package to the project (throguh pkg-config)
|
||||||
|
Add {
|
||||||
|
/// The package to be added
|
||||||
|
package: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Subcommand)]
|
#[derive(clap::Subcommand)]
|
||||||
@@ -71,6 +70,11 @@ enum UtilSubcommand {
|
|||||||
#[arg(value_enum, long, short)]
|
#[arg(value_enum, long, short)]
|
||||||
shell: ShellCompletions,
|
shell: ShellCompletions,
|
||||||
},
|
},
|
||||||
|
/// Generate compile_commands.json for IDE support
|
||||||
|
GenCompileCommands {
|
||||||
|
/// The build mode to generate for
|
||||||
|
mode: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, clap::ValueEnum)]
|
#[derive(Clone, clap::ValueEnum)]
|
||||||
@@ -91,7 +95,9 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Subcommand::Init => {
|
Subcommand::Init => {
|
||||||
if let Err(e) = create_project(".") {
|
let root = std::env::current_dir().expect("the current working directory");
|
||||||
|
let path = root.file_name().expect("some file name");
|
||||||
|
if let Err(e) = create_project(path.to_string_lossy().to_string()) {
|
||||||
eprintln!("Error initializing project: {e}");
|
eprintln!("Error initializing project: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
@@ -156,6 +162,12 @@ impl App {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UtilSubcommand::GenCompileCommands { mode } => {
|
||||||
|
if let Err(e) = gen_compile_commands(&mode) {
|
||||||
|
eprintln!("Error generating compile commands: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Subcommand::List => {
|
Subcommand::List => {
|
||||||
if let Err(e) = list() {
|
if let Err(e) = list() {
|
||||||
@@ -163,9 +175,9 @@ impl App {
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Subcommand::Test { mode } => {
|
Subcommand::Add { package } => {
|
||||||
if let Err(e) = test(mode) {
|
if let Err(e) = add_package(&package) {
|
||||||
eprintln!("Error running tests: {e}");
|
eprintln!("Error adding package {package} to project: {e}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,11 +185,105 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test(mode: Option<String>) -> std::io::Result<()> {
|
fn add_package(package: &str) -> std::io::Result<()> {
|
||||||
for entry in glob("src/tests/*.c").expect("a valid glob pattern") {
|
let status = Command::new("pkg-config")
|
||||||
if let Ok(file) = entry {}
|
.arg("--exists")
|
||||||
|
.arg(package)
|
||||||
|
.status()
|
||||||
|
.map_err(|_| {
|
||||||
|
std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
"pkg-config not found - please install it first",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
eprintln!(
|
||||||
|
" {} pkg-config could not find package '{package}'",
|
||||||
|
"Error".red().bold()
|
||||||
|
);
|
||||||
|
eprintln!(
|
||||||
|
" {} try installing the development package for '{package}', e.g.:",
|
||||||
|
"Hint".yellow().bold()
|
||||||
|
);
|
||||||
|
eprintln!(" Arch: sudo pacman -S {package}");
|
||||||
|
eprintln!(" Debian: sudo apt install lib{package}-dev");
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut conf = get_config().ok_or(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
"no Pallet.toml found in local directory",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let deps = conf.dependencies.get_or_insert_with(Vec::new);
|
||||||
|
|
||||||
|
if deps.contains(&package.to_owned()) {
|
||||||
|
println!(
|
||||||
|
" {} '{package}' is already a dependency",
|
||||||
|
"Warning".yellow().bold()
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
deps.push(package.to_owned());
|
||||||
|
|
||||||
|
std::fs::write(
|
||||||
|
"Pallet.toml",
|
||||||
|
toml::to_string_pretty(&conf).expect("valid TOML"),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
println!(" {} {package}", "Added".green().bold());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_compile_commands(mode: &Option<String>) -> std::io::Result<()> {
|
||||||
|
let conf = get_config().ok_or(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
"no Pallet.toml found in current directory",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let build_config = conf.get_or_default(mode).ok_or(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::NotFound,
|
||||||
|
"build layout not found",
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let compiler = conf.compiler.as_deref().unwrap_or("gcc");
|
||||||
|
let cwd = std::env::current_dir()?;
|
||||||
|
let obj_dir = format!("target/{}/obj", build_config.name);
|
||||||
|
|
||||||
|
let source_files: Vec<PathBuf> = glob("src/*.c")
|
||||||
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, format!("{e}")))?
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let entries: Vec<serde_json::Value> = source_files
|
||||||
|
.iter()
|
||||||
|
.map(|src| {
|
||||||
|
let stem = src.file_stem().unwrap().to_string_lossy();
|
||||||
|
let obj = format!("{}/{}.o", obj_dir, stem);
|
||||||
|
let command = std::iter::once(compiler.to_string())
|
||||||
|
.chain(build_config.args.iter().cloned())
|
||||||
|
.chain(["-c".to_string(), src.to_string_lossy().to_string()])
|
||||||
|
.chain(["-o".to_string(), obj])
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
serde_json::json!({
|
||||||
|
"directory": cwd.to_string_lossy(),
|
||||||
|
"file": cwd.join(src).to_string_lossy(),
|
||||||
|
"command": command
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let json = serde_json::to_string_pretty(&entries)
|
||||||
|
.map_err(|e| std::io::Error::other(format!("{e}")))?;
|
||||||
|
|
||||||
|
std::fs::write("compile_commands.json", json)?;
|
||||||
|
|
||||||
|
println!(" {} compile_commands.json", "Generated".green().bold());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,55 +374,132 @@ fn build(mode: &Option<String>, force_recompile: bool) -> std::io::Result<()> {
|
|||||||
|
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
std::fs::create_dir_all(format!("target/{}", build_config.name))?;
|
let obj_dir = format!("target/{}/obj", build_config.name);
|
||||||
|
|
||||||
let hash = hash_src_tree()?;
|
std::fs::create_dir_all(&obj_dir)?;
|
||||||
|
|
||||||
let old_compute_path = PathBuf::from(format!("target/{}/.build_hash", build_config.name));
|
let compiler = conf.compiler.as_deref().unwrap_or("gcc");
|
||||||
|
|
||||||
if old_compute_path.exists() && !force_recompile {
|
let mut extra_compile_flags: Vec<String> = Vec::new();
|
||||||
let text = std::fs::read_to_string(old_compute_path)?;
|
let mut extra_link_flags: Vec<String> = Vec::new();
|
||||||
|
|
||||||
if hash.trim() == text.trim() {
|
if let Some(deps) = &conf.dependencies
|
||||||
println!(
|
&& !deps.is_empty()
|
||||||
" {} (code not changed, recompile not made)",
|
|
||||||
"Finished".green().bold()
|
|
||||||
);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut command = Command::new(conf.compiler.as_deref().unwrap_or("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 {
|
let cflags = Command::new("pkg-config")
|
||||||
command.arg(path.to_string_lossy().to_string());
|
.arg("--cflags")
|
||||||
|
.args(deps)
|
||||||
|
.output()
|
||||||
|
.map_err(|_| {
|
||||||
|
std::io::Error::new(std::io::ErrorKind::NotFound, "pkg-config not found")
|
||||||
|
})?;
|
||||||
|
if !cflags.status.success() {
|
||||||
|
return Err(std::io::Error::other(
|
||||||
|
"pkg-config --cflags failed - is the package installed?",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let libs = Command::new("pkg-config")
|
||||||
|
.arg("--libs")
|
||||||
|
.args(deps)
|
||||||
|
.output()
|
||||||
|
.map_err(|_| {
|
||||||
|
std::io::Error::new(std::io::ErrorKind::NotFound, "pkg-config not found")
|
||||||
|
})?;
|
||||||
|
if !libs.status.success() {
|
||||||
|
return Err(std::io::Error::other(
|
||||||
|
"pkg-config --libs failed - is the pacakge installed?",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
extra_compile_flags = String::from_utf8_lossy(&cflags.stdout)
|
||||||
|
.split_whitespace()
|
||||||
|
.map(str::to_owned)
|
||||||
|
.collect();
|
||||||
|
extra_link_flags = String::from_utf8_lossy(&libs.stdout)
|
||||||
|
.split_whitespace()
|
||||||
|
.map(str::to_owned)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
let source_files: Vec<PathBuf> = glob("src/*.c")
|
||||||
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, format!("{e}")))?
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut any_changed = false;
|
||||||
|
|
||||||
|
for src in &source_files {
|
||||||
|
let stem = src.file_stem().unwrap().to_string_lossy();
|
||||||
|
let obj_path = format!("{}/{}.o", obj_dir, stem);
|
||||||
|
let hash_path = format!("{}/{}.c.hash", obj_dir, stem);
|
||||||
|
|
||||||
|
let hash = hash_file(src)?;
|
||||||
|
|
||||||
|
let needs_compile = force_recompile
|
||||||
|
|| match std::fs::read_to_string(&hash_path) {
|
||||||
|
Ok(old) => old.trim() != hash.trim(),
|
||||||
|
Err(_) => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if needs_compile {
|
||||||
|
println!(" {} {}", "Compiling".green().bold(), src.display());
|
||||||
|
|
||||||
|
let status = Command::new(compiler)
|
||||||
|
.args(&build_config.args)
|
||||||
|
.args(&extra_compile_flags)
|
||||||
|
.arg("-c")
|
||||||
|
.arg(src)
|
||||||
|
.arg("-o")
|
||||||
|
.arg(&obj_path)
|
||||||
|
.status()?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
return Err(std::io::Error::other(format!(
|
||||||
|
"failed to compile {}",
|
||||||
|
src.display()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::fs::write(&hash_path, &hash)?;
|
||||||
|
any_changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
command
|
let binary = format!("target/{}/{}", build_config.name, conf.name);
|
||||||
.arg("-o")
|
let binary_exists = PathBuf::from(&binary).exists();
|
||||||
.arg(format!("target/{}/{}", build_config.name, conf.name));
|
|
||||||
|
|
||||||
let status = command.status()?;
|
if !any_changed && binary_exists {
|
||||||
|
println!(
|
||||||
|
" {} (code not changed, recompile not made)",
|
||||||
|
"Finished".green().bold()
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let obj_files: Vec<String> = source_files
|
||||||
|
.iter()
|
||||||
|
.map(|src| {
|
||||||
|
let stem = src.file_stem().unwrap().to_string_lossy();
|
||||||
|
format!("{}/{}.o", obj_dir, stem)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let status = Command::new(compiler)
|
||||||
|
.args(&obj_files)
|
||||||
|
.args(&extra_link_flags)
|
||||||
|
.arg("-o")
|
||||||
|
.arg(&binary)
|
||||||
|
.status()?;
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
return Err(std::io::Error::new(
|
return Err(std::io::Error::other("linking failed"));
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
format!("compiler exited with status {status}"),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fs::write(format!("target/{}/.build_hash", build_config.name), hash)?;
|
|
||||||
|
|
||||||
let stop = start.elapsed();
|
let stop = start.elapsed();
|
||||||
|
|
||||||
|
gen_compile_commands(mode)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
" {} '{}' profile for project '{}' in {:.2}s",
|
" {} '{}' profile for project '{}' in {:.2}s",
|
||||||
"Finished".green().bold(),
|
"Finished".green().bold(),
|
||||||
@@ -328,16 +511,9 @@ fn build(mode: &Option<String>, force_recompile: bool) -> std::io::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_src_tree() -> std::io::Result<String> {
|
fn hash_file(path: &Path) -> std::io::Result<String> {
|
||||||
let mut hashes = String::new();
|
let contents = std::fs::read_to_string(path)?;
|
||||||
for entry in glob("src/**/*").expect("a valid glob pattern") {
|
Ok(sha256::digest(contents))
|
||||||
if let Ok(file) = entry {
|
|
||||||
let text = std::fs::read_to_string(file)?;
|
|
||||||
let hash = sha256::digest(text);
|
|
||||||
hashes.push_str(hash.as_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(sha256::digest(hashes))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(mode: &Option<String>, args: Option<Vec<String>>) -> std::io::Result<()> {
|
fn run(mode: &Option<String>, args: Option<Vec<String>>) -> std::io::Result<()> {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ pub struct Config {
|
|||||||
pub authors: Option<Vec<String>>,
|
pub authors: Option<Vec<String>>,
|
||||||
/// Build configs
|
/// Build configs
|
||||||
pub build: Vec<BuildConf>,
|
pub build: Vec<BuildConf>,
|
||||||
|
/// Build dependnecies verified by pkg-config
|
||||||
|
pub dependencies: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
#include <assert.h>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
assert((1 + 1) == 2);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user