Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "star-setup"
version = "0.4.8"
version = "0.4.9"
edition = "2021"
repository = "https://github.com/star-setup/core"
description = "Lightweight CLI to clone, configure, and wire single or multi-repo ecosystems"
Expand Down
39 changes: 39 additions & 0 deletions src/build/cmake/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::{build::common::run_timed, ctx::RunCtx, resolve::ResolvedArgs};
use std::path::Path;

/// Runs `CMake` configuration and optionally builds the project in `build_path`.
/// # Errors
/// Returns an error if any `CMake` command fails.
pub fn cmake_build(
args: &ResolvedArgs,
build_path: &Path,
mono: bool,
ctx: &mut RunCtx<'_, '_>,
) -> Result<(), String> {
let build_type_flag = format!("-DCMAKE_BUILD_TYPE={}", args.build.build_type.to_cmake());
let mut cmake_cmd = if mono {
vec!["cmake", "-DBUILD_LOCAL=ON", &build_type_flag, ".."]
} else {
vec!["cmake", "..", &build_type_flag]
};
cmake_cmd.extend(args.build.cmake_flags.iter().map(String::as_str));

run_timed(&cmake_cmd, Some(build_path), "CMake configure", ctx)?;

if !args.build.no_build {
writeln!(ctx.io.output, "Building project").ok();
run_timed(
&[
"cmake",
"--build",
".",
"--config",
args.build.build_type.to_cmake(),
],
Some(build_path),
"CMake build",
ctx,
)?;
}
Ok(())
}
49 changes: 49 additions & 0 deletions src/build/cmake/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::{
build::common::write_mono_repo_config,
ctx::{IoCtx, RunFlags},
};
use std::path::Path;

/// Generates a root `CMakeLists.txt` wiring all repositories as subdirectories.
/// # Errors
/// Returns an error if the `CMakeLists.txt` file cannot be written to `mono_dir`
pub fn create_mono_repo_cmakelists(
mono_dir: &Path,
repos: &[String],
io: &mut IoCtx<'_>,
flags: RunFlags,
) -> Result<(), String> {
writeln!(io.output, " Creating CMake configuration").ok();
write_mono_repo_config(
mono_dir,
repos,
io,
flags,
"CMakeLists.txt",
|modules| modules.join("\n "),
|modules_cmake| {
format!(
"cmake_minimum_required(VERSION 3.23)

project(star_setup LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)

set(MONO_REPO_MODULES
{modules_cmake}
)

foreach(module IN LISTS MONO_REPO_MODULES)
if(EXISTS \"${{CMAKE_CURRENT_SOURCE_DIR}}/repos/${{module}}/CMakeLists.txt\")
add_subdirectory(repos/${{module}})
else()
message(WARNING \"Module ${{module}} not found or missing CMakeLists.txt\")
endif()
endforeach()

set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER \"External\")
"
)
},
)
}
4 changes: 4 additions & 0 deletions src/build/cmake/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod build;
pub use build::cmake_build;
pub mod config;
pub use config::create_mono_repo_cmakelists;
58 changes: 58 additions & 0 deletions src/build/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::{
ctx::{IoCtx, RunCtx, RunFlags},
repository::repo_dir_name,
utils::{dry_run_or_do, report_summary},
};
use std::{fs::write, path::Path};

/// Runs `cmd`, timing it if `ctx.flags.timing` is set.
/// # Errors
/// Returns an error if the command fails.
pub fn run_timed(
cmd: &[&str],
cwd: Option<&Path>,
label: &str,
ctx: &mut RunCtx<'_, '_>,
) -> Result<(), String> {
crate::time!(ctx.flags.timing, ctx.io.output, label, {
ctx.runner.run(cmd, cwd, ctx.flags, ctx.io.output)?;
});
Ok(())
}

/// Shared helper to generate, write, and log monorepo build configuration files.
/// # Errors
/// Returns an error if the config file cannot be written to `mono_dir`.
pub fn write_mono_repo_config(
mono_dir: &Path,
repos: &[String],
io: &mut IoCtx<'_>,
flags: RunFlags,
filename: &str,
format_modules: impl Fn(&[String]) -> String,
render_template: impl Fn(&str) -> String,
) -> Result<(), String> {
let module_names: Vec<String> = repos.iter().map(|r| repo_dir_name(r)).collect();
let modules_str = format_modules(&module_names);
let content = render_template(&modules_str);
let file_path = mono_dir.join(filename);

dry_run_or_do(
"create file",
"Creating",
&file_path,
io,
flags,
&format!("Generate {filename}"),
|| write(&file_path, content).map_err(|e| e.to_string()),
)?;

report_summary(
io,
flags,
&format!("create root {filename} at {}\n", mono_dir.display()),
&format!("Created root {filename} at {}\n", mono_dir.display()),
);

Ok(())
}
2 changes: 1 addition & 1 deletion src/cli/build/detect.rs → src/build/detect.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{cli::BuildSystem, ctx::RunCtx, prompts::ask_choice};
use crate::{build::BuildSystem, ctx::RunCtx, prompts::ask_choice};
use std::path::{Path, PathBuf};

fn pick_build_system(
Expand Down
74 changes: 74 additions & 0 deletions src/build/dispatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::{
build::{
cmake_build, create_mono_repo_cmakelists, create_mono_repo_mesonbuild,
create_mono_repo_package_json, hoist_wraps, meson_build, npm_build, BuildSystem,
BuildSystem::Cmake, BuildSystem::Meson, BuildSystem::Npm,
},
ctx::RunCtx,
repository::repo_dir_name,
resolve::ResolvedArgs,
};
use std::{
collections::HashMap,
path::{Path, PathBuf},
};

/// Detects and dispatches to the appropriate build system.
/// # Errors
/// Returns an error if detection or the build system command fails.
pub fn build_project(
args: &ResolvedArgs,
build_path: &Path,
source_path: &Path,
build_system: BuildSystem,
mono: bool,
ctx: &mut RunCtx<'_, '_>,
) -> Result<(), String> {
let result = match build_system {
BuildSystem::Cmake => cmake_build(args, build_path, mono, ctx),
BuildSystem::Meson => meson_build(args, build_path, source_path, ctx),
BuildSystem::Npm => npm_build(args, source_path, mono, ctx),
};
writeln!(ctx.io.output).ok();
result
}

/// Generates root build configuration files for the mono-repo.
/// # Errors
/// Returns an error if config file generation or wrap hoisting fails.
pub fn generate_mono_config(
build_system: BuildSystem,
mono_repo_path: &std::path::Path,
repos_path: &std::path::Path,
repo_dirs: &[PathBuf],
repos: &[String],
ctx: &mut RunCtx<'_, '_>,
) -> Result<Option<HashMap<String, String>>, String> {
writeln!(ctx.io.output, " Creating mono-repo configuration").ok();
match build_system {
Cmake => {
create_mono_repo_cmakelists(mono_repo_path, repos, &mut ctx.io, ctx.flags)?;
Ok(None)
}
Meson => {
let map = hoist_wraps(repos_path, repo_dirs, &mut ctx.io, ctx.flags)?;
let subproject_names: Vec<String> = repos
.iter()
.map(|r| {
let dir = repo_dir_name(r);
map
.iter()
.find(|(_, v)| *v == &dir)
.map(|(k, _)| k.clone())
.unwrap_or(dir)
})
.collect();
create_mono_repo_mesonbuild(mono_repo_path, &subproject_names, &mut ctx.io, ctx.flags)?;
Ok(Some(map))
}
Npm => {
create_mono_repo_package_json(mono_repo_path, repos_path, repos, &mut ctx.io, ctx.flags)?;
Ok(None)
}
}
}
37 changes: 37 additions & 0 deletions src/build/meson/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::{build::common::run_timed, ctx::RunCtx, resolve::ResolvedArgs};
use std::path::Path;

fn to_str(path: &Path) -> Result<&str, String> {
path
.to_str()
.ok_or_else(|| format!("Invalid path: {}", path.display()))
}

/// Runs Meson configuration and optionally builds the project in `build_path`.
/// # Errors
/// Returns an error if any Meson command fails.
pub fn meson_build(
args: &ResolvedArgs,
build_path: &Path,
source_path: &Path,
ctx: &mut RunCtx<'_, '_>,
) -> Result<(), String> {
let buildtype_flag = format!("--buildtype={}", args.build.build_type.to_meson());
let mut meson_cmd = vec!["meson", "setup"];
meson_cmd.push(&buildtype_flag);
meson_cmd.push(to_str(build_path)?);
meson_cmd.push(to_str(source_path)?);
meson_cmd.extend(args.build.meson_flags.iter().map(String::as_str));

run_timed(&meson_cmd, None, "Meson setup", ctx)?;
if !args.build.no_build {
writeln!(ctx.io.output, "Building project").ok();
run_timed(
&["meson", "compile", "-C", to_str(build_path)?],
None,
"Meson compile",
ctx,
)?;
}
Ok(())
}
48 changes: 48 additions & 0 deletions src/build/meson/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use crate::{
build::common::write_mono_repo_config,
ctx::{IoCtx, RunFlags},
};
use std::path::Path;

/// Generates a root `meson.build` wiring all repositories as subprojects.
/// # Errors
/// Returns an error if the `meson.build` file cannot be written to `mono_dir`.
pub fn create_mono_repo_mesonbuild(
mono_dir: &Path,
repos: &[String],
io: &mut IoCtx<'_>,
flags: RunFlags,
) -> Result<(), String> {
writeln!(io.output, " Creating Meson configuration").ok();
write_mono_repo_config(
mono_dir,
repos,
io,
flags,
"meson.build",
|modules| {
modules
.iter()
.map(|m| format!(" '{m}'"))
.collect::<Vec<_>>()
.join(",\n")
},
|modules_meson| {
format!(
"project('star_setup', 'cpp',
default_options: ['cpp_std=c++20'],
subproject_dir: 'repos'
)

modules = [
{modules_meson},
]

foreach module : modules
subproject(module)
endforeach
"
)
},
)
}
6 changes: 6 additions & 0 deletions src/build/meson/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod build;
pub use build::meson_build;
pub mod config;
pub use config::create_mono_repo_mesonbuild;
pub mod wraps;
pub use wraps::{hoist_wraps, parse_project_name, parse_provide_pairs};
File renamed without changes.
19 changes: 19 additions & 0 deletions src/build/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pub mod types;
pub use types::{BuildSystem, BuildType};
pub mod detect;
pub use detect::{detect_build_system, detect_mono_build_system};
pub mod dispatch;
pub use dispatch::{build_project, generate_mono_config};
pub mod common;

pub mod cmake;
pub use cmake::{cmake_build, create_mono_repo_cmakelists};
pub mod meson;
pub use meson::{
create_mono_repo_mesonbuild, hoist_wraps, meson_build, parse_project_name, parse_provide_pairs,
};
pub mod npm;
pub use npm::{
create_mono_repo_package_json, generate_watch_scripts, maybe_open_dev_server, npm_build,
open_dev_server, open_watch_scripts, read_package_json, resolve_dev_command,
};
35 changes: 35 additions & 0 deletions src/build/npm/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::{
build::{common::run_timed, read_package_json},
ctx::RunCtx,
resolve::ResolvedArgs,
};
use std::path::Path;

/// Runs `npm install` and optionally builds the project in `source_path`.
/// # Errors
/// Returns an error if any npm command fails.
pub fn npm_build(
args: &ResolvedArgs,
source_path: &Path,
is_mono: bool,
ctx: &mut RunCtx<'_, '_>,
) -> Result<(), String> {
writeln!(ctx.io.output, "Installing dependencies").ok();
run_timed(&["npm", "install"], Some(source_path), "npm install", ctx)?;
if !args.build.no_build && !is_mono {
let has_build = read_package_json(source_path, "skipping build", &mut ctx.io, ctx.flags)
.is_some_and(|j| j.get("scripts").and_then(|s| s.get("build")).is_some());
if has_build {
writeln!(ctx.io.output, "Building project").ok();
run_timed(
&["npm", "run", "build"],
Some(source_path),
"npm build",
ctx,
)?;
} else if ctx.flags.verbose {
writeln!(ctx.io.output, " No build script found, skipping build").ok();
}
}
Ok(())
}
Loading
Loading