diff --git a/.llm/BETA_1_TOOLING_HARDENING.md b/.llm/BETA_1_TOOLING_HARDENING.md index bd585e5..a5ef6d8 100644 --- a/.llm/BETA_1_TOOLING_HARDENING.md +++ b/.llm/BETA_1_TOOLING_HARDENING.md @@ -17,6 +17,11 @@ complete and the full release gate passes near publication. - `glagol new --template binary|library|workspace` supports the existing binary scaffold plus checkable/testable library and local workspace scaffolds. +- `scripts/install.sh` installs `bin/glagol`, `share/slovo/std/*.slo`, and + `share/slovo/runtime/runtime.c` under a configurable prefix. +- Installed `glagol` discovers both standard-library source modules and the + runtime C input relative to the executable, with `SLOVO_STD_PATH`, + `SLOVO_RUNTIME_C`, `GLAGOL_RUNTIME_C`, and `GLAGOL_CLANG` override paths. - The release gate prints a concise success line after docs, formatting, tests, promotion, binary, and LLVM smoke checks pass. @@ -26,15 +31,11 @@ complete and the full release gate passes near publication. - no networking or runtime resource model - no package registry behavior - no stable ABI/layout promise -- no stable install layout promise until the install-path portion of this - tooling bundle is finished +- no operating-system package-manager integration +- no stable install layout promise beyond this beta toolchain layout ## Remaining Before Tagging `1.0.0-beta.1` -- document and gate public install layout for `glagol`, `runtime/`, and - `lib/std` -- add a minimal install or packaging command/script if the existing build flow - is not enough - rerender publication PDFs only if documentation release text changes - run the full release gate from a clean checkout state - +- decide whether this tooling bundle is sufficient for the `1.0.0-beta.1` tag diff --git a/README.md b/README.md index 1a32b15..6e6cde7 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,26 @@ Create alternate project shapes: ./compiler/target/debug/glagol new workspace-demo --template workspace ``` +Install the current checkout: + +```bash +PREFIX="$HOME/.local" ./scripts/install.sh +``` + +The installed layout is: + +```text +/bin/glagol +/share/slovo/std/*.slo +/share/slovo/runtime/runtime.c +``` + +Installed `glagol` discovers `share/slovo/std` and +`share/slovo/runtime/runtime.c` relative to its executable. `SLOVO_STD_PATH` +can still override standard-library search, `SLOVO_RUNTIME_C` or +`GLAGOL_RUNTIME_C` can override the runtime C input, and `GLAGOL_CLANG` can +select the Clang-compatible compiler. + ## Documentation - [Language Manifest](docs/language/MANIFEST.md) diff --git a/compiler/src/main.rs b/compiler/src/main.rs index 7781045..bcb4016 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -2222,6 +2222,32 @@ fn parse_test_count(output: &str, suffix: &str) -> Option { } fn runtime_path() -> PathBuf { + runtime_path_candidates() + .into_iter() + .find(|path| path.is_file()) + .unwrap_or_else(checkout_runtime_path) +} + +fn runtime_path_candidates() -> Vec { + let mut candidates = Vec::new(); + if let Some(path) = env::var_os("SLOVO_RUNTIME_C") { + candidates.push(PathBuf::from(path)); + } + if let Some(path) = env::var_os("GLAGOL_RUNTIME_C") { + candidates.push(PathBuf::from(path)); + } + if let Ok(exe) = env::current_exe() { + if let Some(bin_dir) = exe.parent() { + candidates.push(bin_dir.join("runtime/runtime.c")); + candidates.push(bin_dir.join("../runtime/runtime.c")); + candidates.push(bin_dir.join("../share/slovo/runtime/runtime.c")); + } + } + candidates.push(checkout_runtime_path()); + candidates +} + +fn checkout_runtime_path() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).join("../runtime/runtime.c") } diff --git a/compiler/tests/dx_v1_7.rs b/compiler/tests/dx_v1_7.rs index 5b7b036..a8cddd5 100644 --- a/compiler/tests/dx_v1_7.rs +++ b/compiler/tests/dx_v1_7.rs @@ -125,6 +125,64 @@ fn run_forwards_program_arguments_when_host_toolchain_is_available() { } } +#[test] +fn installed_layout_discovers_std_and_runtime_without_checkout_paths() { + let prefix = unique_path("installed-layout"); + let bin_dir = prefix.join("bin"); + let std_dir = prefix.join("share/slovo/std"); + let runtime_dir = prefix.join("share/slovo/runtime"); + fs::create_dir_all(&bin_dir).expect("create installed bin dir"); + fs::create_dir_all(&std_dir).expect("create installed std dir"); + fs::create_dir_all(&runtime_dir).expect("create installed runtime dir"); + + let installed_glagol = bin_dir.join(format!("glagol{}", std::env::consts::EXE_SUFFIX)); + fs::copy(env!("CARGO_BIN_EXE_glagol"), &installed_glagol).expect("copy glagol"); + make_executable(&installed_glagol); + + let repo_root = Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("compiler has repo parent"); + fs::copy( + repo_root.join("runtime/runtime.c"), + runtime_dir.join("runtime.c"), + ) + .expect("copy runtime"); + for entry in fs::read_dir(repo_root.join("lib/std")).expect("read std dir") { + let entry = entry.expect("read std entry"); + let path = entry.path(); + if path.extension().and_then(|ext| ext.to_str()) == Some("slo") { + fs::copy( + &path, + std_dir.join(path.file_name().expect("std file name")), + ) + .expect("copy std module"); + } + } + + let project = write_project( + "installed-layout-project", + &[], + "(module main)\n\n(import std.io (print_string_zero))\n(import std.string (concat))\n\n(fn main () -> i32\n (print_string_zero (concat \"installed\" \"-ok\")))\n", + ); + + let check = run_installed_glagol(&installed_glagol, ["check".as_ref(), project.as_os_str()]); + assert_success("installed layout check", &check); + + let run = run_installed_glagol(&installed_glagol, ["run".as_ref(), project.as_os_str()]); + if host_clang_available() { + assert_success("installed layout run", &run); + assert_eq!(String::from_utf8_lossy(&run.stdout), "installed-ok\n"); + assert!(run.stderr.is_empty(), "installed run wrote stderr"); + } else { + assert_exit_code("installed layout run without clang", &run, 3); + assert_stderr_contains( + "installed layout run without clang", + &run, + "ToolchainUnavailable", + ); + } +} + #[test] fn new_library_template_creates_checkable_testable_library_project() { let project = unique_path("library-template"); @@ -329,6 +387,7 @@ fn release_gate_script_exists_and_names_required_commands() { let script = Path::new("../scripts/release-gate.sh"); let text = fs::read_to_string(script).expect("read release gate script"); assert!(text.contains("git diff --check")); + assert!(text.contains("scripts/install.sh")); assert!(text.contains("cargo fmt --check")); assert!(text.contains("cargo test")); assert!(text.contains("dx_v1_7")); @@ -390,6 +449,39 @@ where .expect("run glagol") } +fn run_installed_glagol(binary: &Path, args: I) -> Output +where + I: IntoIterator, + S: AsRef, +{ + Command::new(binary) + .env_remove("SLOVO_STD_PATH") + .env_remove("SLOVO_RUNTIME_C") + .env_remove("GLAGOL_RUNTIME_C") + .args(args) + .output() + .expect("run installed glagol") +} + +fn host_clang_available() -> bool { + let clang = std::env::var("GLAGOL_CLANG").unwrap_or_else(|_| "clang".to_string()); + Command::new(clang) + .arg("--version") + .output() + .map(|output| output.status.success()) + .unwrap_or(false) +} + +fn make_executable(path: &Path) { + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut permissions = fs::metadata(path).expect("stat executable").permissions(); + permissions.set_mode(0o755); + fs::set_permissions(path, permissions).expect("chmod executable"); + } +} + fn assert_success(context: &str, output: &Output) { assert!( output.status.success(), diff --git a/docs/POST_BETA_ROADMAP.md b/docs/POST_BETA_ROADMAP.md index 25992f5..6d42cb4 100644 --- a/docs/POST_BETA_ROADMAP.md +++ b/docs/POST_BETA_ROADMAP.md @@ -37,10 +37,9 @@ Work: - keep PDF rendering explicit and non-mutating by default Current main-branch progress after `1.0.0-beta`: `glagol run`, -`glagol clean`, `glagol new --template binary|library|workspace`, README -coverage, focused DX tests, and a concise release-gate success line are -implemented. Install-path polish remains in this tooling bundle before a -`1.0.0-beta.1` tag. +`glagol clean`, `glagol new --template binary|library|workspace`, +`scripts/install.sh`, installed std/runtime discovery, README coverage, +focused DX tests, and a concise release-gate success line are implemented. Why first: it reduces friction for every later feature and gives users a better way to exercise the beta. diff --git a/docs/compiler/RELEASE_NOTES.md b/docs/compiler/RELEASE_NOTES.md index 51e8c29..2a846b9 100644 --- a/docs/compiler/RELEASE_NOTES.md +++ b/docs/compiler/RELEASE_NOTES.md @@ -19,6 +19,10 @@ future `1.0.0-beta.1` bundle: - `glagol clean ` removes generated `.slovo/build` artifacts - `glagol new --template binary|library|workspace` adds library and local workspace scaffolds beside the existing binary default +- `scripts/install.sh` installs `bin/glagol`, `share/slovo/std`, and + `share/slovo/runtime/runtime.c` +- installed native builds discover the runtime C input relative to the + executable, with `SLOVO_RUNTIME_C` and `GLAGOL_RUNTIME_C` overrides - the release gate prints a concise final success summary This is a toolchain workflow slice only. It does not claim a new stable ABI, diff --git a/docs/language/RELEASE_NOTES.md b/docs/language/RELEASE_NOTES.md index 9ec2361..c9e6bf3 100644 --- a/docs/language/RELEASE_NOTES.md +++ b/docs/language/RELEASE_NOTES.md @@ -23,6 +23,11 @@ Post-beta main currently contains tooling hardening intended for a future - `glagol clean ` removes generated `.slovo/build` artifacts - `glagol new --template binary|library|workspace` scaffolds binary projects, library projects, and local package workspaces using existing manifest rules +- `scripts/install.sh` installs the compiler, `lib/std`, and runtime C input + under a configurable prefix +- installed `glagol` discovers `share/slovo/std` and + `share/slovo/runtime/runtime.c` relative to its executable, with environment + overrides still available - the release gate prints a concise final success line after all checks pass This unreleased slice does not add source-language syntax, stable ABI/layout diff --git a/docs/language/SPEC-v1.md b/docs/language/SPEC-v1.md index 538eb20..5d9e58d 100644 --- a/docs/language/SPEC-v1.md +++ b/docs/language/SPEC-v1.md @@ -953,6 +953,20 @@ scaffold shapes. `binary` is the default project with `src/main.slo`. creates a local two-package workspace using the existing `[workspace]`, `[package]`, and local path dependency rules. +`scripts/install.sh` installs the beta toolchain layout under a configurable +prefix: + +```text +/bin/glagol +/share/slovo/std/*.slo +/share/slovo/runtime/runtime.c +``` + +Installed `glagol` must discover the standard-library sources and runtime C +input relative to its executable. `SLOVO_STD_PATH` remains the standard-library +source override. `SLOVO_RUNTIME_C` and `GLAGOL_RUNTIME_C` may override the +runtime C input for native builds. + ## 4.5 v2.0.0-beta.1 Experimental Integration Readiness Status: current experimental Slovo-side release contract, released 2026-05-17. diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..f403344 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd -- "${script_dir}/.." && pwd)" +prefix="${PREFIX:-${HOME}/.local}" +profile="release" +build=1 + +usage() { + cat <<'USAGE' +usage: scripts/install.sh [--prefix ] [--debug] [--no-build] + +Installs the Slovo toolchain layout: + + /bin/glagol + /share/slovo/std/*.slo + /share/slovo/runtime/runtime.c + +Set PREFIX instead of --prefix if preferred. +USAGE +} + +while [ "$#" -gt 0 ]; do + case "$1" in + --prefix) + if [ "$#" -lt 2 ]; then + echo "--prefix requires a directory" >&2 + exit 2 + fi + prefix="$2" + shift 2 + ;; + --debug) + profile="debug" + shift + ;; + --no-build) + build=0 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "unexpected argument: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if [ "${build}" -eq 1 ]; then + cargo build --manifest-path "${repo_root}/compiler/Cargo.toml" --profile "${profile}" +fi + +binary="${repo_root}/compiler/target/${profile}/glagol" +if [ ! -x "${binary}" ]; then + echo "missing built compiler binary: ${binary}" >&2 + echo "run cargo build --manifest-path compiler/Cargo.toml --profile ${profile}" >&2 + exit 1 +fi + +bin_dir="${prefix}/bin" +std_dir="${prefix}/share/slovo/std" +runtime_dir="${prefix}/share/slovo/runtime" +doc_dir="${prefix}/share/doc/slovo" + +install -d "${bin_dir}" "${std_dir}" "${runtime_dir}" "${doc_dir}" +install -m 755 "${binary}" "${bin_dir}/glagol" +install -m 644 "${repo_root}/runtime/runtime.c" "${runtime_dir}/runtime.c" + +find "${repo_root}/lib/std" -maxdepth 1 -type f -name '*.slo' -print0 | + while IFS= read -r -d '' module; do + install -m 644 "${module}" "${std_dir}/$(basename "${module}")" + done + +install -m 644 "${repo_root}/README.md" "${doc_dir}/README.md" +install -m 644 "${repo_root}/LICENSE-MIT" "${doc_dir}/LICENSE-MIT" +install -m 644 "${repo_root}/LICENSE-APACHE" "${doc_dir}/LICENSE-APACHE" + +cat <