add content to deps
This commit is contained in:
parent
d2bf437f53
commit
f28d7515b8
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -1886,6 +1886,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"prometeu-core",
|
"prometeu-core",
|
||||||
"prometeu-deps",
|
"prometeu-deps",
|
||||||
|
"prometeu-languages-registry",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
]
|
]
|
||||||
@ -1923,7 +1924,11 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"camino",
|
"camino",
|
||||||
"prometeu-core",
|
"prometeu-core",
|
||||||
|
"prometeu-language-api",
|
||||||
|
"prometeu-languages-registry",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1980,9 +1985,20 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "prometeu-language-api"
|
name = "prometeu-language-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prometeu-language-pbs"
|
||||||
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"prometeu-language-api",
|
||||||
"thiserror",
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prometeu-languages-registry"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"prometeu-language-api",
|
||||||
|
"prometeu-language-pbs",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
"crates/compiler/languages/prometeu-languages-registry",
|
||||||
|
"crates/compiler/languages/prometeu-language-pbs",
|
||||||
|
|
||||||
"crates/compiler/prometeu-build-pipeline",
|
"crates/compiler/prometeu-build-pipeline",
|
||||||
"crates/compiler/prometeu-bytecode",
|
"crates/compiler/prometeu-bytecode",
|
||||||
"crates/compiler/prometeu-core",
|
"crates/compiler/prometeu-core",
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "prometeu-language-pbs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
prometeu-language-api = { path = "../../prometeu-language-api" }
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
use std::sync::OnceLock;
|
||||||
|
use prometeu_language_api::{LanguageSpec, SourcePolicy};
|
||||||
|
|
||||||
|
pub static LANGUAGE_SPEC: OnceLock<LanguageSpec> = OnceLock::new();
|
||||||
|
|
||||||
|
fn registry() -> &'static LanguageSpec {
|
||||||
|
LANGUAGE_SPEC.get_or_init(|| {
|
||||||
|
LanguageSpec {
|
||||||
|
id: "pbs",
|
||||||
|
source_policy: SourcePolicy {
|
||||||
|
extensions: vec!["pbs"],
|
||||||
|
case_sensitive: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
mod language_spec;
|
||||||
|
|
||||||
|
pub use language_spec::LANGUAGE_SPEC;
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "prometeu-languages-registry"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT"
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
prometeu-language-api = { path = "../../prometeu-language-api" }
|
||||||
|
|
||||||
|
prometeu-language-pbs = { path = "../prometeu-language-pbs" }
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
use prometeu_language_api::LanguageSpec;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use prometeu_language_pbs::LANGUAGE_SPEC as PBS_LANGUAGE_SPEC;
|
||||||
|
|
||||||
|
static REGISTRY: OnceLock<HashMap<&'static str, LanguageSpec>> = OnceLock::new();
|
||||||
|
|
||||||
|
fn registry() -> &'static HashMap<&'static str, LanguageSpec> {
|
||||||
|
let pbs = PBS_LANGUAGE_SPEC.get().unwrap();
|
||||||
|
REGISTRY.get_or_init(|| {
|
||||||
|
HashMap::from([
|
||||||
|
(pbs.id, pbs.clone()),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_language_spec(id: &str) -> Option<&LanguageSpec> {
|
||||||
|
registry().get(id)
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
mod language_spec_registry;
|
||||||
|
|
||||||
|
pub use language_spec_registry::get_language_spec;
|
||||||
@ -16,6 +16,7 @@ include = ["../../VERSION.txt"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
prometeu-deps = { path = "../prometeu-deps" }
|
prometeu-deps = { path = "../prometeu-deps" }
|
||||||
prometeu-core = { path = "../prometeu-core" }
|
prometeu-core = { path = "../prometeu-core" }
|
||||||
|
prometeu-languages-registry = { path = "../languages/prometeu-languages-registry" }
|
||||||
clap = { version = "4.5.54", features = ["derive"] }
|
clap = { version = "4.5.54", features = ["derive"] }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use crate::pipeline::run_phases;
|
|||||||
use crate::{BuildMode, PipelineConfig, PipelineInput, PipelineOutput};
|
use crate::{BuildMode, PipelineConfig, PipelineInput, PipelineOutput};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use prometeu_deps::{load_sources, resolve_project, DepsConfig};
|
use prometeu_deps::{load_sources, resolve_workspace, DepsConfig};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
/// Command line interface for the Prometeu Compiler.
|
/// Command line interface for the Prometeu Compiler.
|
||||||
@ -128,7 +128,7 @@ fn run_pipeline(cfg: PipelineConfig, project_dir: &Path, explain_deps: bool) ->
|
|||||||
registry_dirs: vec![],
|
registry_dirs: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let resolved = resolve_project(&deps_cfg, project_dir)
|
let resolved = resolve_workspace(&deps_cfg, project_dir)
|
||||||
.with_context(|| format!("deps: failed to resolve project at {:?}", project_dir))?;
|
.with_context(|| format!("deps: failed to resolve project at {:?}", project_dir))?;
|
||||||
|
|
||||||
// resolved deve te dar pelo menos:
|
// resolved deve te dar pelo menos:
|
||||||
|
|||||||
@ -30,15 +30,11 @@ impl ProjectCtx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pipeline context (in-memory state).
|
|
||||||
/// Arena-friendly: uses Vec + IDs as the main storage.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PipelineCtx {
|
pub struct PipelineCtx {
|
||||||
pub source_db: FileDB,
|
pub source_db: FileDB,
|
||||||
pub interner: NameInterner,
|
pub interner: NameInterner,
|
||||||
pub diagnostics: Vec<Diagnostic>,
|
pub diagnostics: Vec<Diagnostic>,
|
||||||
|
|
||||||
/// Projects in stack order (deps first).
|
|
||||||
pub projects: Vec<ProjectCtx>,
|
pub projects: Vec<ProjectCtx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +56,8 @@ impl PipelineCtx {
|
|||||||
pub fn init_projects_from_stack(&mut self, stack: &BuildStack) {
|
pub fn init_projects_from_stack(&mut self, stack: &BuildStack) {
|
||||||
self.projects.clear();
|
self.projects.clear();
|
||||||
self.projects.reserve(stack.projects.len());
|
self.projects.reserve(stack.projects.len());
|
||||||
for p in &stack.projects {
|
for project_id in &stack.projects {
|
||||||
self.projects.push(ProjectCtx::new(p.project_id));
|
self.projects.push(ProjectCtx::new(project_id.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,8 @@ pub mod config;
|
|||||||
pub mod ctx;
|
pub mod ctx;
|
||||||
pub mod pipeline;
|
pub mod pipeline;
|
||||||
pub mod phases;
|
pub mod phases;
|
||||||
pub use config::*;
|
|
||||||
|
|
||||||
|
pub use config::*;
|
||||||
pub use ctx::*;
|
pub use ctx::*;
|
||||||
pub use pipeline::*;
|
pub use pipeline::*;
|
||||||
pub use cli::run;
|
pub use cli::run;
|
||||||
|
|||||||
@ -13,14 +13,15 @@ pub fn run(_cfg: &PipelineConfig, input: &PipelineInput, ctx: &mut PipelineCtx)
|
|||||||
let is_empty = ctx.projects[i].files.is_empty();
|
let is_empty = ctx.projects[i].files.is_empty();
|
||||||
|
|
||||||
if is_empty {
|
if is_empty {
|
||||||
let proj = &input.stack.projects[i];
|
let project_id = &input.stack.projects[i];
|
||||||
|
let project_name = input.graph.project(project_id).unwrap().name.clone();
|
||||||
|
|
||||||
ctx.push_diagnostic(Diagnostic {
|
ctx.push_diagnostic(Diagnostic {
|
||||||
severity: Severity::Warning,
|
severity: Severity::Warning,
|
||||||
code: "PIPELINE_NO_SOURCES".into(),
|
code: "PIPELINE_NO_SOURCES".into(),
|
||||||
message: format!(
|
message: format!(
|
||||||
"Project '{}' has no source files loaded.",
|
"Project '{}' has no source files loaded.",
|
||||||
proj.name
|
project_name
|
||||||
),
|
),
|
||||||
span: Span::none(),
|
span: Span::none(),
|
||||||
related: vec![],
|
related: vec![],
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
pub mod boot;
|
pub mod boot;
|
||||||
pub mod load_source;
|
pub mod load_source;
|
||||||
pub mod frontend;
|
pub mod language;
|
||||||
pub mod backend;
|
pub mod lowering;
|
||||||
pub mod emit;
|
pub mod emit;
|
||||||
|
|||||||
@ -37,10 +37,10 @@ pub(crate) fn run_phases(cfg: PipelineConfig, input: PipelineInput) -> PipelineO
|
|||||||
phases::load_source::run(&cfg, &input, &mut ctx);
|
phases::load_source::run(&cfg, &input, &mut ctx);
|
||||||
|
|
||||||
// Frontend phase (stub / optional).
|
// Frontend phase (stub / optional).
|
||||||
phases::frontend::run(&cfg, &input, &mut ctx);
|
phases::language::run(&cfg, &input, &mut ctx);
|
||||||
|
|
||||||
// Backend phase (stub).
|
// Backend phase (stub).
|
||||||
phases::backend::run(&cfg, &input, &mut ctx);
|
phases::lowering::run(&cfg, &input, &mut ctx);
|
||||||
|
|
||||||
// Emit phase (stub).
|
// Emit phase (stub).
|
||||||
let artifacts = phases::emit::run(&cfg, &input, &mut ctx);
|
let artifacts = phases::emit::run(&cfg, &input, &mut ctx);
|
||||||
|
|||||||
@ -8,8 +8,12 @@ description = ""
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
prometeu-core = { path = "../prometeu-core" }
|
prometeu-core = { path = "../prometeu-core" }
|
||||||
|
prometeu-language-api = { path = "../prometeu-language-api" }
|
||||||
|
prometeu-languages-registry = { path = "../languages/prometeu-languages-registry" }
|
||||||
anyhow = "1.0.101"
|
anyhow = "1.0.101"
|
||||||
camino = "1.2.2"
|
camino = "1.2.2"
|
||||||
|
walkdir = "2.5.0"
|
||||||
|
serde_json = "1.0.149"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
mod model;
|
mod model;
|
||||||
mod resolve_project;
|
|
||||||
mod load_sources;
|
mod load_sources;
|
||||||
|
mod workspace;
|
||||||
|
|
||||||
pub use resolve_project::resolve_project;
|
pub use workspace::resolve_workspace;
|
||||||
pub use load_sources::load_sources;
|
pub use load_sources::load_sources;
|
||||||
|
|
||||||
pub use model::resolved_project::ResolvedProject;
|
pub use model::manifest::*;
|
||||||
|
pub use model::resolved_project::ResolvedWorkspace;
|
||||||
pub use model::resolved_explanation::ResolveExplanation;
|
pub use model::resolved_explanation::ResolveExplanation;
|
||||||
pub use model::deps_config::DepsConfig;
|
pub use model::deps_config::DepsConfig;
|
||||||
pub use model::project_descriptor::ProjectDescriptor;
|
pub use model::project_descriptor::ProjectDescriptor;
|
||||||
|
|||||||
@ -1,5 +1,97 @@
|
|||||||
use crate::{DepsConfig, LoadedSources, ResolvedProject};
|
use anyhow::{Context, Result};
|
||||||
|
use camino::Utf8PathBuf;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
pub fn load_sources(p0: &DepsConfig, p1: &ResolvedProject) -> anyhow::Result<LoadedSources> {
|
use crate::{
|
||||||
todo!()
|
DepsConfig,
|
||||||
}
|
LoadedFile,
|
||||||
|
LoadedSources,
|
||||||
|
ProjectSources,
|
||||||
|
ResolvedWorkspace,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn load_sources(cfg: &DepsConfig, resolved: &ResolvedWorkspace) -> Result<LoadedSources> {
|
||||||
|
let mut per_project = Vec::with_capacity(resolved.stack.projects.len());
|
||||||
|
|
||||||
|
for project_id in &resolved.stack.projects {
|
||||||
|
let project = resolved
|
||||||
|
.graph
|
||||||
|
.project(project_id)
|
||||||
|
.with_context(|| format!("deps: unknown project_id {:?} in build stack", project_id))?;
|
||||||
|
|
||||||
|
if cfg.explain {
|
||||||
|
eprintln!(
|
||||||
|
"[deps] load_sources: project {}@{} ({:?})",
|
||||||
|
project.name, project.version, project.project_dir
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut files: Vec<LoadedFile> = Vec::new();
|
||||||
|
|
||||||
|
for root in &project.source_roots {
|
||||||
|
let abs_root = project.project_dir.join(root);
|
||||||
|
|
||||||
|
if cfg.explain {
|
||||||
|
eprintln!("[deps] scanning {:?}", abs_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !abs_root.exists() {
|
||||||
|
anyhow::bail!(
|
||||||
|
"deps: source root does not exist for project {}@{}: {:?}",
|
||||||
|
project.name,
|
||||||
|
project.version,
|
||||||
|
abs_root
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk recursively.
|
||||||
|
for entry in WalkDir::new(&abs_root)
|
||||||
|
.follow_links(false)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|e| e.ok())
|
||||||
|
{
|
||||||
|
let ft = entry.file_type();
|
||||||
|
if !ft.is_file() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = entry.path();
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: precisamos mexer no prometeu.json para configurar o frontend do projeto
|
||||||
|
// Filter extensions: start with PBS only.
|
||||||
|
if path.extension().and_then(|s| s.to_str()) != Some("pbs") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to Utf8Path (the best effort) and use a stable "uri".
|
||||||
|
let path_utf8: Utf8PathBuf = match Utf8PathBuf::from_path_buf(path.to_path_buf()) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => {
|
||||||
|
anyhow::bail!("deps: non-utf8 path found while scanning sources: {:?}", path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let text = std::fs::read_to_string(&path_utf8)
|
||||||
|
.with_context(|| format!("deps: failed to read source file {:?}", path_utf8))?;
|
||||||
|
|
||||||
|
// TODO: normalize newlines
|
||||||
|
|
||||||
|
files.push(LoadedFile {
|
||||||
|
uri: path_utf8.to_string(),
|
||||||
|
text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determinism: sort a file list by uri (important for stable builds).
|
||||||
|
files.sort_by(|a, b| a.uri.cmp(&b.uri));
|
||||||
|
|
||||||
|
per_project.push(ProjectSources {
|
||||||
|
project_id: project_id.clone(),
|
||||||
|
files,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(LoadedSources { per_project })
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
use crate::model::project_descriptor::ProjectDescriptor;
|
use prometeu_core::ProjectId;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BuildStack {
|
pub struct BuildStack {
|
||||||
/// deps-first order
|
pub projects: Vec<ProjectId>,
|
||||||
pub projects: Vec<ProjectDescriptor>,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ use camino::Utf8PathBuf;
|
|||||||
|
|
||||||
pub struct DepsConfig {
|
pub struct DepsConfig {
|
||||||
pub explain: bool,
|
pub explain: bool,
|
||||||
// diretórios e política (só deps entende isso)
|
|
||||||
pub cache_dir: Utf8PathBuf,
|
pub cache_dir: Utf8PathBuf,
|
||||||
pub registry_dirs: Vec<Utf8PathBuf>, // ou sources
|
pub registry_dirs: Vec<Utf8PathBuf>, // or sources ?
|
||||||
}
|
}
|
||||||
75
crates/compiler/prometeu-deps/src/model/manifest.rs
Normal file
75
crates/compiler/prometeu-deps/src/model/manifest.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use camino::Utf8PathBuf;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Manifest {
|
||||||
|
pub name: String,
|
||||||
|
pub version: String,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub source_roots: Vec<String>,
|
||||||
|
|
||||||
|
pub language: LanguageDecl,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub deps: Vec<DepDecl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct LanguageDecl {
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum DepDecl {
|
||||||
|
Local {
|
||||||
|
path: String,
|
||||||
|
},
|
||||||
|
Git {
|
||||||
|
git: String,
|
||||||
|
rev: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PrometeuLock {
|
||||||
|
pub schema: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub mappings: Vec<LockMapping>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrometeuLock {
|
||||||
|
pub fn blank() -> Self {
|
||||||
|
Self {
|
||||||
|
schema: 0,
|
||||||
|
mappings: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_git_local_dir(&self, url: &str, rev: &str) -> Option<&String> {
|
||||||
|
self.mappings.iter().find_map(|m| match m {
|
||||||
|
LockMapping::Git {
|
||||||
|
git, rev: r, local_dir
|
||||||
|
} if git == url && r == rev => Some(local_dir),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(tag = "kind", rename_all = "lowercase")]
|
||||||
|
pub enum LockMapping {
|
||||||
|
Git {
|
||||||
|
git: String,
|
||||||
|
rev: String,
|
||||||
|
local_dir: String,
|
||||||
|
},
|
||||||
|
Registry {
|
||||||
|
registry: String,
|
||||||
|
version: String,
|
||||||
|
local_dir: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -8,4 +8,5 @@ pub mod loaded_file;
|
|||||||
pub mod cache_blobs;
|
pub mod cache_blobs;
|
||||||
pub mod resolved_project;
|
pub mod resolved_project;
|
||||||
pub mod resolved_explanation;
|
pub mod resolved_explanation;
|
||||||
pub(crate) mod cache_plan;
|
pub mod cache_plan;
|
||||||
|
pub mod manifest;
|
||||||
@ -1,8 +1,14 @@
|
|||||||
|
use camino::Utf8PathBuf;
|
||||||
use prometeu_core::ProjectId;
|
use prometeu_core::ProjectId;
|
||||||
|
use prometeu_language_api::SourcePolicy;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ProjectDescriptor {
|
pub struct ProjectDescriptor {
|
||||||
pub project_id: ProjectId,
|
pub project_id: ProjectId,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
|
pub project_dir: Utf8PathBuf,
|
||||||
|
pub source_roots: Vec<Utf8PathBuf>,
|
||||||
|
pub language_id: String,
|
||||||
|
pub source_policy: SourcePolicy,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,3 +8,9 @@ pub struct ResolvedGraph {
|
|||||||
// opcional: adjacency list para checks
|
// opcional: adjacency list para checks
|
||||||
pub edges: Vec<Vec<ProjectId>>, // edges[from] = vec[to]
|
pub edges: Vec<Vec<ProjectId>>, // edges[from] = vec[to]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ResolvedGraph {
|
||||||
|
pub fn project(&self, id: &ProjectId) -> Option<&ProjectDescriptor> {
|
||||||
|
self.projects.get(id.0 as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use crate::{BuildStack, ResolvedGraph, ResolveExplanation, CachePlan};
|
use crate::{BuildStack, ResolvedGraph, ResolveExplanation, CachePlan};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ResolvedProject {
|
pub struct ResolvedWorkspace {
|
||||||
pub graph: ResolvedGraph,
|
pub graph: ResolvedGraph,
|
||||||
pub stack: BuildStack,
|
pub stack: BuildStack,
|
||||||
pub explain: Option<ResolveExplanation>
|
pub explain: Option<ResolveExplanation>
|
||||||
|
|||||||
@ -1,8 +0,0 @@
|
|||||||
use crate::{DepsConfig, ResolvedProject};
|
|
||||||
use camino::Utf8Path;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub fn resolve_project(cfg: &DepsConfig, path: &Path) -> anyhow::Result<ResolvedProject> {
|
|
||||||
let project_dir = Utf8Path::from_path(path).unwrap();
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
32
crates/compiler/prometeu-deps/src/workspace/host.rs
Normal file
32
crates/compiler/prometeu-deps/src/workspace/host.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use anyhow::{Context, Result};
|
||||||
|
use camino::{Utf8Path, Utf8PathBuf};
|
||||||
|
|
||||||
|
use crate::workspace::model::DepRef;
|
||||||
|
|
||||||
|
pub trait DepsHost {
|
||||||
|
fn read_to_string(&self, path: &Utf8Path) -> Result<String>;
|
||||||
|
|
||||||
|
// fn ensure_project_local(&self, from_dir: &Utf8Path, dep: &DepRef) -> Result<Utf8PathBuf>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FsHost;
|
||||||
|
|
||||||
|
impl DepsHost for FsHost {
|
||||||
|
fn read_to_string(&self, path: &Utf8Path) -> Result<String> {
|
||||||
|
std::fs::read_to_string(path)
|
||||||
|
.with_context(|| format!("failed to read {:?}", path))
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn ensure_project_local(&self, from_dir: &Utf8Path, dep: &DepRef) -> Result<Utf8PathBuf> {
|
||||||
|
// match dep {
|
||||||
|
// DepRef::Local { path } => {
|
||||||
|
// let joined = from_dir.join(path);
|
||||||
|
// let canon = joined.canonicalize()
|
||||||
|
// .with_context(|| format!("deps: dep path does not exist: {:?}", joined))?;
|
||||||
|
// Utf8PathBuf::from_path_buf(canon)
|
||||||
|
// .map_err(|p| anyhow::anyhow!("deps: non-utf8 dep dir: {:?}", p))
|
||||||
|
// }
|
||||||
|
// _ => unimplemented!(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
6
crates/compiler/prometeu-deps/src/workspace/mod.rs
Normal file
6
crates/compiler/prometeu-deps/src/workspace/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
mod resolve_workspace;
|
||||||
|
mod host;
|
||||||
|
mod model;
|
||||||
|
mod phases;
|
||||||
|
|
||||||
|
pub use resolve_workspace::resolve_workspace;
|
||||||
31
crates/compiler/prometeu-deps/src/workspace/model.rs
Normal file
31
crates/compiler/prometeu-deps/src/workspace/model.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use camino::Utf8PathBuf;
|
||||||
|
use prometeu_core::ProjectId;
|
||||||
|
use prometeu_language_api::SourcePolicy;
|
||||||
|
|
||||||
|
use crate::Manifest;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RawProjectNode {
|
||||||
|
pub dir: Utf8PathBuf,
|
||||||
|
pub manifest_path: Utf8PathBuf,
|
||||||
|
pub manifest: Manifest,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum DepRef {
|
||||||
|
Local {
|
||||||
|
path: Utf8PathBuf
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ProjectNode {
|
||||||
|
pub id: ProjectId,
|
||||||
|
pub dir: Utf8PathBuf,
|
||||||
|
pub name: String,
|
||||||
|
pub version: String,
|
||||||
|
pub source_roots: Vec<Utf8PathBuf>,
|
||||||
|
pub language_id: String,
|
||||||
|
pub deps: Vec<DepRef>,
|
||||||
|
pub source_policy: SourcePolicy,
|
||||||
|
}
|
||||||
131
crates/compiler/prometeu-deps/src/workspace/phases/discover.rs
Normal file
131
crates/compiler/prometeu-deps/src/workspace/phases/discover.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use crate::model::manifest::DepDecl;
|
||||||
|
use crate::workspace::host::DepsHost;
|
||||||
|
use crate::workspace::model::RawProjectNode;
|
||||||
|
use crate::workspace::phases::state::ResolverState;
|
||||||
|
use crate::Manifest;
|
||||||
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
|
use camino::Utf8PathBuf;
|
||||||
|
use serde_json;
|
||||||
|
use std::fs::canonicalize;
|
||||||
|
|
||||||
|
/// Phase 1: Discover all projects in the workspace.
|
||||||
|
///
|
||||||
|
/// - Reads `prometeu.json` from each pending project directory.
|
||||||
|
/// - Parses `Manifest`.
|
||||||
|
/// - Registers the raw node.
|
||||||
|
/// - Enqueues local-path deps for discovery (v0).
|
||||||
|
///
|
||||||
|
/// Does NOT:
|
||||||
|
/// - assign ProjectId
|
||||||
|
/// - build edges
|
||||||
|
/// - validate versions
|
||||||
|
pub fn discover(
|
||||||
|
cfg: &crate::DepsConfig,
|
||||||
|
host: &dyn DepsHost,
|
||||||
|
state: &mut ResolverState,
|
||||||
|
) -> Result<()> {
|
||||||
|
while let Some(canon_dir) = state.pending.pop_front() {
|
||||||
|
// de-dup by directory
|
||||||
|
if state.raw_by_dir.contains_key(&canon_dir) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let manifest_path = canon_dir.join("prometeu.json");
|
||||||
|
if !manifest_path.exists() || !manifest_path.is_file() {
|
||||||
|
bail!(
|
||||||
|
"deps: manifest not found: expected a file {:?} (project dir {:?})",
|
||||||
|
manifest_path,
|
||||||
|
canon_dir
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.explain {
|
||||||
|
eprintln!("[deps][discover] reading {:?}", manifest_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = host
|
||||||
|
.read_to_string(&manifest_path)
|
||||||
|
.with_context(|| format!("deps: failed to read manifest {:?}", manifest_path))?;
|
||||||
|
|
||||||
|
let manifest: Manifest = serde_json::from_str(&text)
|
||||||
|
.with_context(|| format!("deps: invalid manifest JSON {:?}", manifest_path))?;
|
||||||
|
|
||||||
|
// Register raw node
|
||||||
|
let raw_idx = state.raw.len();
|
||||||
|
state.raw.push(RawProjectNode {
|
||||||
|
dir: canon_dir.clone(),
|
||||||
|
manifest_path: manifest_path.clone(),
|
||||||
|
manifest: manifest.clone(),
|
||||||
|
});
|
||||||
|
state.raw_by_dir.insert(canon_dir.clone(), raw_idx);
|
||||||
|
|
||||||
|
for dep in &manifest.deps {
|
||||||
|
match dep {
|
||||||
|
DepDecl::Local { path } => {
|
||||||
|
let dep_dir = canon_dir.join(path);
|
||||||
|
|
||||||
|
let dep_dir_std = dep_dir.canonicalize().with_context(|| {
|
||||||
|
format!(
|
||||||
|
"deps: dep path does not exist: {:?} (from {:?})",
|
||||||
|
dep_dir, canon_dir
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let dep_dir_canon = Utf8PathBuf::from_path_buf(dep_dir_std)
|
||||||
|
.map_err(|p| anyhow!("deps: non-utf8 dep dir: {:?}", p))?;
|
||||||
|
|
||||||
|
if cfg.explain {
|
||||||
|
eprintln!("[deps][discover] local dep '{}' -> {:?}", path, dep_dir_canon);
|
||||||
|
}
|
||||||
|
state.pending.push_back(dep_dir_canon);
|
||||||
|
}
|
||||||
|
|
||||||
|
DepDecl::Git { git, rev } => {
|
||||||
|
let Some(rev) = rev.as_deref() else {
|
||||||
|
bail!(
|
||||||
|
"deps: git dependency '{}' requires an explicit 'rev' (commit hash) for now",
|
||||||
|
git
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(local_dir) = state.lock.lookup_git_local_dir(git, rev) else {
|
||||||
|
bail!(
|
||||||
|
"deps: git dependency requires prometeu.lock mapping, but entry not found: git='{}' rev='{}'",
|
||||||
|
git,
|
||||||
|
rev
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// canonicalize the lock-provided local dir to keep identity stable
|
||||||
|
let local_dir_std = canonicalize(local_dir)
|
||||||
|
.with_context(|| format!("deps: prometeu.lock local_dir does not exist: {:?}", local_dir))?;
|
||||||
|
|
||||||
|
let local_dir_canon = Utf8PathBuf::from_path_buf(local_dir_std)
|
||||||
|
.map_err(|p| anyhow!("deps: non-utf8 lock local_dir: {:?}", p))?;
|
||||||
|
|
||||||
|
// validate manifest exists at the mapped project root
|
||||||
|
// (this check should not belong here, but it is ok)
|
||||||
|
let mapped_manifest = local_dir_canon.join("prometeu.json");
|
||||||
|
if !mapped_manifest.exists() || !mapped_manifest.is_file() {
|
||||||
|
bail!(
|
||||||
|
"deps: prometeu.lock maps git dep to {:?}, but manifest is missing: {:?}",
|
||||||
|
local_dir_canon,
|
||||||
|
mapped_manifest
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.explain {
|
||||||
|
eprintln!(
|
||||||
|
"[deps][discover] git dep '{}' rev '{}' -> {:?}",
|
||||||
|
git, rev, local_dir_canon
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.pending.push_back(local_dir_canon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
|
use prometeu_core::ProjectId;
|
||||||
|
|
||||||
|
use crate::workspace::model::DepRef;
|
||||||
|
use crate::workspace::phases::state::ResolverState;
|
||||||
|
|
||||||
|
/// Phase 3: Localize dependencies and build graph edges.
|
||||||
|
///
|
||||||
|
/// For each project node:
|
||||||
|
/// - For each DepRef:
|
||||||
|
/// - host.ensure_project_local(from_dir, dep) -> dep_dir (local on disk)
|
||||||
|
/// - map dep_dir to ProjectId via st.by_dir
|
||||||
|
/// - st.edges[from].push(dep_id)
|
||||||
|
///
|
||||||
|
/// v0 policy:
|
||||||
|
/// - Only DepRef::LocalPath is supported.
|
||||||
|
/// - Git/Registry cause a hard error (future extension point).
|
||||||
|
pub fn localize(cfg: &crate::DepsConfig, state: &mut ResolverState) -> Result<()> {
|
||||||
|
// Reset edges (allows re-run / deterministic behavior)
|
||||||
|
for e in &mut state.edges {
|
||||||
|
e.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for from_idx in 0..state.nodes.len() {
|
||||||
|
let from_id: ProjectId = state.nodes[from_idx].id;
|
||||||
|
let from_dir = state.nodes[from_idx].dir.clone();
|
||||||
|
|
||||||
|
if cfg.explain {
|
||||||
|
eprintln!(
|
||||||
|
"[deps][localize] from id={:?} dir={:?}",
|
||||||
|
from_id, from_dir
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone deps to avoid borrow conflicts (simple + safe for now)
|
||||||
|
let deps = state.nodes[from_idx].deps.clone();
|
||||||
|
|
||||||
|
for dep in deps {
|
||||||
|
match &dep {
|
||||||
|
DepRef::Local {
|
||||||
|
path
|
||||||
|
} => {
|
||||||
|
let dep_id = state.by_dir.get(path).copied().with_context(|| {
|
||||||
|
format!(
|
||||||
|
"deps: localized dep dir {:?} was not discovered; \
|
||||||
|
ensure the dep has a prometeu.json and is reachable via local paths",
|
||||||
|
path
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
state.edges[from_id.0 as usize].push(dep_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional: keep edges deterministic
|
||||||
|
state.edges[from_id.0 as usize].sort_by_key(|id| id.0);
|
||||||
|
state.edges[from_id.0 as usize].dedup();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
use crate::model::manifest::DepDecl;
|
||||||
|
use crate::workspace::model::{DepRef, ProjectNode};
|
||||||
|
use crate::workspace::phases::state::ResolverState;
|
||||||
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
|
use camino::Utf8PathBuf;
|
||||||
|
use prometeu_core::ProjectId;
|
||||||
|
use prometeu_languages_registry::get_language_spec;
|
||||||
|
use std::fs::canonicalize;
|
||||||
|
|
||||||
|
/// Phase 2: Materialize projects (allocate ProjectId / arena nodes).
|
||||||
|
///
|
||||||
|
/// Inputs:
|
||||||
|
/// - st.raw (RawProjectNode: dir + manifest)
|
||||||
|
///
|
||||||
|
/// Outputs:
|
||||||
|
/// - st.nodes (ProjectNode arena)
|
||||||
|
/// - st.by_dir (dir -> ProjectId)
|
||||||
|
/// - st.edges (allocated adjacency lists, empty for now)
|
||||||
|
/// - st.root (ProjectId for root_dir)
|
||||||
|
///
|
||||||
|
/// Does NOT:
|
||||||
|
/// - resolve deps to local dirs (that's phase localize)
|
||||||
|
/// - validate version conflicts/cycles
|
||||||
|
/// - resolve language/source policy
|
||||||
|
pub fn materialize(cfg: &crate::DepsConfig, state: &mut ResolverState) -> Result<()> {
|
||||||
|
// Reset materialized state (allows rerun in future refactors/tests)
|
||||||
|
state.nodes.clear();
|
||||||
|
state.by_dir.clear();
|
||||||
|
state.edges.clear();
|
||||||
|
state.root = None;
|
||||||
|
|
||||||
|
state.nodes.reserve(state.raw.len());
|
||||||
|
state.edges.reserve(state.raw.len());
|
||||||
|
|
||||||
|
for (idx, raw) in state.raw.iter().enumerate() {
|
||||||
|
let id = ProjectId(idx as u32);
|
||||||
|
|
||||||
|
// Default source roots if omitted
|
||||||
|
let source_roots: Vec<Utf8PathBuf> = raw
|
||||||
|
.manifest
|
||||||
|
.source_roots
|
||||||
|
.iter()
|
||||||
|
.map(|root| Utf8PathBuf::from(root))
|
||||||
|
.collect();
|
||||||
|
if source_roots.is_empty() {
|
||||||
|
bail!(
|
||||||
|
"deps: no source roots specified for project {}",
|
||||||
|
raw.manifest.name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert DepDecl -> DepRef (no localization yet)
|
||||||
|
let mut deps: Vec<DepRef> = Vec::with_capacity(raw.manifest.deps.len());
|
||||||
|
for d in &raw.manifest.deps {
|
||||||
|
match d {
|
||||||
|
DepDecl::Local { path } => {
|
||||||
|
let joined = raw.dir.join(path);
|
||||||
|
let dir_std = joined.canonicalize()
|
||||||
|
.with_context(|| format!("deps: local dep path does not exist: {:?} (from {:?})", joined, raw.dir))?;
|
||||||
|
|
||||||
|
let dir_canon = Utf8PathBuf::from_path_buf(dir_std)
|
||||||
|
.map_err(|p| anyhow!("deps: non-utf8 dep dir: {:?}", p))?;
|
||||||
|
deps.push(DepRef::Local {
|
||||||
|
path: dir_canon
|
||||||
|
});
|
||||||
|
}
|
||||||
|
DepDecl::Git { git, rev } => {
|
||||||
|
let Some(rev) = rev.as_deref() else {
|
||||||
|
bail!(
|
||||||
|
"deps: git dependency '{}' requires an explicit 'rev' (commit hash) for now",
|
||||||
|
git
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(local_dir) = state.lock.lookup_git_local_dir(git, rev) else {
|
||||||
|
bail!(
|
||||||
|
"deps: git dependency requires prometeu.lock mapping, but entry not found: git='{}' rev='{}'",
|
||||||
|
git,
|
||||||
|
rev
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// canonicalize the lock-provided local dir to keep identity stable
|
||||||
|
let path = canonicalize(local_dir).with_context(|| {
|
||||||
|
format!(
|
||||||
|
"deps: prometeu.lock local_dir does not exist: {:?}",
|
||||||
|
local_dir
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let local_dir_canon = Utf8PathBuf::from_path_buf(path)
|
||||||
|
.map_err(|p| anyhow!("deps: non-utf8 lock local_dir: {:?}", p))?;
|
||||||
|
|
||||||
|
deps.push(DepRef::Local {
|
||||||
|
path: local_dir_canon,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.explain {
|
||||||
|
eprintln!(
|
||||||
|
"[deps][materialize] id={:?} {}@{} dir={:?} language={}",
|
||||||
|
id, raw.manifest.name, raw.manifest.version, raw.dir, raw.manifest.language.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let source_policy = get_language_spec(raw.manifest.language.id.as_str())
|
||||||
|
.map(|spec| spec.source_policy.clone())
|
||||||
|
.ok_or(anyhow!(
|
||||||
|
"deps: unknown language spec: {}",
|
||||||
|
raw.manifest.language.id
|
||||||
|
))?;
|
||||||
|
|
||||||
|
// Record node
|
||||||
|
state.nodes.push(ProjectNode {
|
||||||
|
id,
|
||||||
|
dir: raw.dir.clone(),
|
||||||
|
name: raw.manifest.name.clone(),
|
||||||
|
version: raw.manifest.version.clone(),
|
||||||
|
source_roots,
|
||||||
|
language_id: raw.manifest.language.id.clone(),
|
||||||
|
deps,
|
||||||
|
source_policy,
|
||||||
|
});
|
||||||
|
|
||||||
|
state.by_dir.insert(raw.dir.clone(), id);
|
||||||
|
state.edges.push(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine root id
|
||||||
|
if let Some(root_id) = state.by_dir.get(&state.root_dir).copied() {
|
||||||
|
state.root = Some(root_id);
|
||||||
|
} else {
|
||||||
|
// This should never happen if seed/discover worked.
|
||||||
|
// Keep it as a hard failure (in a later validate phase you can convert to a nicer diagnostic).
|
||||||
|
anyhow::bail!(
|
||||||
|
"deps: root project dir {:?} was not discovered/materialized",
|
||||||
|
state.root_dir
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
10
crates/compiler/prometeu-deps/src/workspace/phases/mod.rs
Normal file
10
crates/compiler/prometeu-deps/src/workspace/phases/mod.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
mod run_all;
|
||||||
|
mod state;
|
||||||
|
mod discover;
|
||||||
|
mod materialize;
|
||||||
|
mod localize;
|
||||||
|
mod validate;
|
||||||
|
mod policy;
|
||||||
|
mod stack;
|
||||||
|
|
||||||
|
pub use run_all::run_all;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
use crate::DepsConfig;
|
||||||
|
use crate::workspace::phases::state::ResolverState;
|
||||||
|
|
||||||
|
pub(crate) fn policy(p0: &DepsConfig, p1: &mut ResolverState) -> _ {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
use camino::Utf8Path;
|
||||||
|
use crate::{DepsConfig, ResolvedWorkspace};
|
||||||
|
use crate::workspace::host::FsHost;
|
||||||
|
use crate::workspace::phases::{discover, localize, materialize, policy, stack, state, validate};
|
||||||
|
|
||||||
|
pub fn run_all(cfg: &DepsConfig, fs_host: &FsHost, root_dir: &Utf8Path) -> Result<ResolvedWorkspace> {
|
||||||
|
let mut state = state::seed(cfg, root_dir)?;
|
||||||
|
discover::discover(cfg, fs_host, &mut state)?;
|
||||||
|
materialize::materialize(cfg, &mut state)?;
|
||||||
|
localize::localize(cfg, &mut state)?;
|
||||||
|
validate::validate(cfg, &mut state)?;
|
||||||
|
policy::policy(cfg, &mut state)?;
|
||||||
|
stack::stack(cfg, &mut state)
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
use crate::{DepsConfig, ResolvedWorkspace};
|
||||||
|
use crate::workspace::phases::state::ResolverState;
|
||||||
|
|
||||||
|
pub(crate) fn stack(cfg: &DepsConfig, state: &mut ResolverState) -> Result<ResolvedWorkspace, _> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
58
crates/compiler/prometeu-deps/src/workspace/phases/state.rs
Normal file
58
crates/compiler/prometeu-deps/src/workspace/phases/state.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use camino::{Utf8Path, Utf8PathBuf};
|
||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
use anyhow::Context;
|
||||||
|
use crate::workspace::model::{RawProjectNode, ProjectNode};
|
||||||
|
use prometeu_core::ProjectId;
|
||||||
|
use crate::PrometeuLock;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
pub struct ResolverState {
|
||||||
|
pub root_dir: Utf8PathBuf,
|
||||||
|
|
||||||
|
// phase1 output
|
||||||
|
pub raw: Vec<RawProjectNode>,
|
||||||
|
pub raw_by_dir: HashMap<Utf8PathBuf, usize>,
|
||||||
|
pub pending: VecDeque<Utf8PathBuf>,
|
||||||
|
|
||||||
|
// phase2+
|
||||||
|
pub nodes: Vec<ProjectNode>,
|
||||||
|
pub by_dir: HashMap<Utf8PathBuf, ProjectId>,
|
||||||
|
pub edges: Vec<Vec<ProjectId>>,
|
||||||
|
|
||||||
|
pub root: Option<ProjectId>,
|
||||||
|
|
||||||
|
pub lock: PrometeuLock,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seed(_cfg: &crate::DepsConfig, root_dir: &Utf8Path) -> anyhow::Result<ResolverState> {
|
||||||
|
let path_buf = root_dir.canonicalize()?;
|
||||||
|
let root_dir_canon = Utf8PathBuf::from_path_buf(path_buf)
|
||||||
|
.map_err(|p| anyhow::anyhow!("deps: non-utf8 root dir: {:?}", p))?;
|
||||||
|
|
||||||
|
let lock_path = root_dir_canon.join("prometeu.lock");
|
||||||
|
let lock = if lock_path.exists() {
|
||||||
|
let txt = std::fs::read_to_string(&lock_path)?;
|
||||||
|
serde_json::from_str::<PrometeuLock>(&txt)
|
||||||
|
.with_context(|| format!("invalid prometeu.lock at {:?}", lock_path))?
|
||||||
|
} else {
|
||||||
|
PrometeuLock::blank()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut pending = VecDeque::new();
|
||||||
|
pending.push_back(root_dir_canon.clone());
|
||||||
|
|
||||||
|
Ok(ResolverState {
|
||||||
|
root_dir: root_dir_canon.clone(),
|
||||||
|
raw: vec![],
|
||||||
|
raw_by_dir: HashMap::new(),
|
||||||
|
pending,
|
||||||
|
|
||||||
|
nodes: vec![],
|
||||||
|
by_dir: HashMap::new(),
|
||||||
|
edges: vec![],
|
||||||
|
|
||||||
|
root: None,
|
||||||
|
|
||||||
|
lock,
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
use crate::DepsConfig;
|
||||||
|
use crate::workspace::phases::state::ResolverState;
|
||||||
|
|
||||||
|
pub(crate) fn validate(p0: &DepsConfig, p1: &mut ResolverState) -> _ {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use camino::Utf8Path;
|
||||||
|
|
||||||
|
use crate::{DepsConfig, ResolvedWorkspace};
|
||||||
|
use crate::workspace::host::FsHost;
|
||||||
|
|
||||||
|
pub fn resolve_workspace(cfg: &DepsConfig, root_dir: &Utf8Path) -> Result<ResolvedWorkspace> {
|
||||||
|
let host = FsHost;
|
||||||
|
crate::workspace::phases::run_all(cfg, &host, root_dir)
|
||||||
|
}
|
||||||
@ -7,9 +7,4 @@ description = "Canonical language contract for Prometeu Backend: identifiers, re
|
|||||||
repository = "https://github.com/prometeu/runtime"
|
repository = "https://github.com/prometeu/runtime"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
|
||||||
thiserror = "1"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
serde = ["dep:serde"]
|
|
||||||
|
|||||||
21
crates/compiler/prometeu-language-api/src/language_spec.rs
Normal file
21
crates/compiler/prometeu-language-api/src/language_spec.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SourcePolicy {
|
||||||
|
pub extensions: Vec<&'static str>,
|
||||||
|
pub case_sensitive: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SourcePolicy {
|
||||||
|
pub fn matches_ext(&self, ext: &str) -> bool {
|
||||||
|
if self.case_sensitive {
|
||||||
|
self.extensions.iter().any(|e| *e == ext)
|
||||||
|
} else {
|
||||||
|
self.extensions.iter().any(|e| e.eq_ignore_ascii_case(ext))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LanguageSpec {
|
||||||
|
pub id: &'static str,
|
||||||
|
pub source_policy: SourcePolicy,
|
||||||
|
}
|
||||||
@ -1 +1,3 @@
|
|||||||
|
mod language_spec;
|
||||||
|
|
||||||
|
pub use language_spec::*;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user