(target: &std::path::Path)
| 145 | } |
| 146 | |
| 147 | pub fn from_path(target: &std::path::Path) -> anyhow::Result<Self> { |
| 148 | // Allow the config to specified either with a full path to a config file or to a folder |
| 149 | // containing a `config.yml`. |
| 150 | let (root, config_path) = match target.extension().map_or(false, |x| x == "yml") { |
| 151 | true => { |
| 152 | let root = target.parent().unwrap_or(std::path::Path::new(".")).to_owned(); |
| 153 | (root, target.to_owned()) |
| 154 | } |
| 155 | false => (target.to_owned(), target.join("config.yml")), |
| 156 | }; |
| 157 | |
| 158 | // If config file does not exist, attempt to generate one for the user. |
| 159 | if root.exists() && !config_path.exists() { |
| 160 | eprintln!( |
| 161 | "WARNING: Config file not found. Generating default config at: {}", |
| 162 | config_path.display() |
| 163 | ); |
| 164 | crate::genconfig::generate_and_save(target, false)?; |
| 165 | } |
| 166 | |
| 167 | let mut config: Self = serde_yaml::from_slice( |
| 168 | &std::fs::read(&config_path) |
| 169 | .with_context(|| format!("failed to read '{}'", config_path.display()))?, |
| 170 | ) |
| 171 | .with_context(|| format!("error parsing: {}", config_path.display()))?; |
| 172 | config.path = config_path; |
| 173 | |
| 174 | let num_entry_regions = config.memory_map.values().filter(|x| x.is_entry).count(); |
| 175 | if num_entry_regions > 1 { |
| 176 | anyhow::bail!("expected at most one memory region (got {num_entry_regions})"); |
| 177 | } |
| 178 | |
| 179 | // Try to automatically set entry region if none was specified. |
| 180 | if num_entry_regions == 0 && config.entry_point.is_none() { |
| 181 | // Find the file-backed region with the lowest base address. |
| 182 | let mut min_region = None; |
| 183 | for (name, region) in config.memory_map.iter() { |
| 184 | let file = match ®ion.file { |
| 185 | Some(name) => name, |
| 186 | None => continue, |
| 187 | }; |
| 188 | |
| 189 | let (_, prev_region) = min_region.unwrap_or((name, region)); |
| 190 | if file != prev_region.file.as_ref().unwrap() { |
| 191 | anyhow::bail!( |
| 192 | "explicit entry point must be set when there are multiple file regions." |
| 193 | ); |
| 194 | } |
| 195 | if prev_region.base_addr < region.base_addr { |
| 196 | // Previous region was at a lower base address. |
| 197 | continue; |
| 198 | } |
| 199 | min_region = Some((name, region)); |
| 200 | } |
| 201 | |
| 202 | let (name, _) = min_region.ok_or_else(|| { |
| 203 | anyhow::format_err!("No entrypoints and no file backed regions were defined") |
| 204 | })?; |
nothing calls this directly
no test coverage detected