(args: &[String], cwd: &Path)
| 130 | |
| 131 | pub(crate) fn spec_command(args: &[String], cwd: &Path) -> Result<(), String> { |
| 132 | let target_fps = parse_flag_value(args, "--target-fps") |
| 133 | .map(|raw| { |
| 134 | raw.parse::<f64>() |
| 135 | .map_err(|_| format!("invalid --target-fps `{raw}`")) |
| 136 | }) |
| 137 | .transpose()? |
| 138 | .unwrap_or(60.0); |
| 139 | if !target_fps.is_finite() || target_fps <= 0.0 { |
| 140 | return Err("--target-fps must be > 0".to_string()); |
| 141 | } |
| 142 | let boot_scene = parse_boot_scene_flag(args)?; |
| 143 | |
| 144 | let project_dir = parse_flag_value(args, "--path") |
| 145 | .map(|p| resolve_local_path(&p, cwd)) |
| 146 | .unwrap_or_else(|| cwd.to_path_buf()); |
| 147 | let project_dir = project_dir.canonicalize().unwrap_or(project_dir); |
| 148 | let project_cfg = load_project_toml(&project_dir) |
| 149 | .map_err(|err| format!("failed to load project.toml: {err}"))?; |
| 150 | let output_dir = ensure_profiling_output_dir(&project_dir)?.join("spec"); |
| 151 | fs::create_dir_all(&output_dir) |
| 152 | .map_err(|err| format!("failed to create {}: {err}", output_dir.display()))?; |
| 153 | let samples_path = output_dir.join("samples.csv"); |
| 154 | let frames_path = output_dir.join("frames.csv"); |
| 155 | let markers_path = output_dir.join("markers.jsonl"); |
| 156 | fs::write(&samples_path, "") |
| 157 | .map_err(|err| format!("failed to reset {}: {err}", samples_path.display()))?; |
| 158 | fs::write(&markers_path, "") |
| 159 | .map_err(|err| format!("failed to reset {}: {err}", markers_path.display()))?; |
| 160 | fs::write(&frames_path, "") |
| 161 | .map_err(|err| format!("failed to reset {}: {err}", frames_path.display()))?; |
| 162 | |
| 163 | update_workspace_vscode_linked_projects(&workspace_root(), &project_dir)?; |
| 164 | update_project_vscode_linked_projects(&project_dir)?; |
| 165 | ensure_source_overrides(&project_dir) |
| 166 | .map_err(|err| format!("failed to refresh source overrides: {err}"))?; |
| 167 | |
| 168 | log_step("Building Spec Scripts"); |
| 169 | compile_scripts_with_profile(&project_dir, ScriptsBuildProfile::Spec).map_err(|err| { |
| 170 | format!( |
| 171 | "scripts pipeline failed for {}: {err}", |
| 172 | project_dir.display() |
| 173 | ) |
| 174 | })?; |
| 175 | log_done("Spec Scripts Built"); |
| 176 | |
| 177 | let dev_runner_dir = project_dir.join(".perro").join("dev_runner"); |
| 178 | let target_dir = project_dir.join("target"); |
| 179 | log_step("Building Spec Runner"); |
| 180 | let mut build_cmd = Command::new("cargo"); |
| 181 | build_cmd |
| 182 | .arg("build") |
| 183 | .arg("--release") |
| 184 | .env("CARGO_TARGET_DIR", &target_dir) |
| 185 | .current_dir(&dev_runner_dir); |
| 186 | let mut features = vec!["mem_profile"]; |
| 187 | if project_cfg.steam.enabled { |
| 188 | features.push("steamworks"); |
| 189 | } |
no test coverage detected