Skip to content

Commit

Permalink
Generate dependencies files
Browse files Browse the repository at this point in the history
Fixes #11
  • Loading branch information
AngheloAlf committed Mar 12, 2024
1 parent 2eca6c4 commit 2e4e532
Show file tree
Hide file tree
Showing 17 changed files with 12,475 additions and 31 deletions.
37 changes: 37 additions & 0 deletions docs/file_format/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,43 @@ A positive integer or `null`.

`null`

## `d_path`

Output path for the `.d` (dependencies) file.

A dependencies file consists on a file that lists required files to build a
given file. This kind of dependency files can be consumed by build systems like
`make` or `ninja`.

The generated `.d` file will list all the paths listed by every segment as
required to build a given [`target_path`](#target_path).

This file is generated only if `d_path` is specified.

This option requires [`target_path`](#target_path).

### Example

```yaml
settings:
d_path: linker_scripts/game.d
target_path: build/game.elf
```

## `target_path`

The path to the file that will result of linking, usually an `.elf` file.

Currently only used by the [`d_path`](#d_path) setting.

### Example

```yaml
settings:
d_path: linker_scripts/game.d
target_path: build/game.elf
```

## `sections_allowlist`

A list of sections to that should be preserved during linking.
Expand Down
10 changes: 10 additions & 0 deletions slinky-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ fn main() {
// println!("settings {:#?}", document.settings);

let mut writer = slinky::LinkerWriter::new(&document.settings);
let mut dependencies_writer = slinky::DependenciesWriter::new(&document.settings);
writer.begin_sections();
for segment in &document.segments {
writer.add_segment(segment);
dependencies_writer.add_segment(segment);
}
writer.end_sections();

Expand All @@ -40,4 +42,12 @@ fn main() {
} else {
println!("{}", writer.export_as_string());
}

if let Some(d_path) = &document.settings.d_path {
if let Some(target_path) = &document.settings.target_path {
dependencies_writer
.save_dependencies_file(d_path, target_path)
.expect("Error writing dependencies file");
}
}
}
1 change: 1 addition & 0 deletions slinky/src/absent_nullable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ where
}
}

// TODO: consider changing the "default" callbacks to return a result
impl<T> AbsentNullable<T> {
pub fn get_non_null<F>(self, name: &str, default: F) -> Result<T, SlinkyError>
where
Expand Down
118 changes: 118 additions & 0 deletions slinky/src/dependencies_writer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/* SPDX-FileCopyrightText: © 2024 decompals */
/* SPDX-License-Identifier: MIT */

use std::io::Write;
use std::path::{Path, PathBuf};

use crate::{utils, FileInfo, FileKind, Segment, Settings, SlinkyError};

pub struct DependenciesWriter<'a> {
required_files: Vec<PathBuf>,

settings: &'a Settings,
}

impl<'a> DependenciesWriter<'a> {
pub fn new(settings: &'a Settings) -> Self {
Self {
required_files: Vec::new(),
settings,
}
}

pub fn add_segment(&mut self, segment: &Segment) {
for file in &segment.files {
self.emit_file(file, segment, &self.settings.base_path);
}
}

pub fn save_dependencies_file(
&mut self,
path: &Path,
target_path: &Path,
) -> Result<(), SlinkyError> {
let mut f = utils::create_file_and_parents(path)?;

if let Err(e) = write!(f, "{}:", target_path.display()) {
return Err(SlinkyError::FailedFileWrite {
path: path.to_path_buf(),
description: e.to_string(),
contents: target_path.display().to_string(),
});
}

for p in &self.required_files {
if let Err(e) = write!(f, " \\\n {}", p.display()) {
return Err(SlinkyError::FailedFileWrite {
path: path.to_path_buf(),
description: e.to_string(),
contents: p.display().to_string(),
});
}
}

if let Err(e) = write!(f, "\n\n") {
return Err(SlinkyError::FailedFileWrite {
path: path.to_path_buf(),
description: e.to_string(),
contents: "".to_string(),
});
}

for p in &self.required_files {
if let Err(e) = writeln!(f, "{}:", p.display()) {
return Err(SlinkyError::FailedFileWrite {
path: path.to_path_buf(),
description: e.to_string(),
contents: p.display().to_string(),
});
}
}

Ok(())
}

#[must_use]
pub fn export_as_string(&self, target_path: &Path) -> String {
let mut ret = String::new();

ret += &format!("{}:", target_path.display());

for p in &self.required_files {
ret += &format!(" \\\n {}", p.display());
}

ret += "\n\n";

for p in &self.required_files {
ret += &format!("{}:\n", p.display());
}

ret
}
}

// internal functions
impl<'a> DependenciesWriter<'a> {
fn emit_file(&mut self, file: &FileInfo, segment: &Segment, base_path: &Path) {
// TODO: figure out glob support
match file.kind {
FileKind::Object | FileKind::Archive => {
let mut path = base_path.to_path_buf();
path.extend(&file.path);

self.required_files.push(path);
}
FileKind::Pad | FileKind::LinkerOffset => (),
FileKind::Group => {
let mut new_base_path = base_path.to_path_buf();

new_base_path.extend(&file.dir);

for file_of_group in &file.files {
self.emit_file(file_of_group, segment, &new_base_path);
}
}
}
}
}
3 changes: 3 additions & 0 deletions slinky/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ pub enum SlinkyError {

#[error("Field '{name}' is required")]
MissingRequiredField { name: String },

#[error("Field '{required}' is required if field '{other}' is given")]
MissingRequiredFieldCombo { required: String, other: String },
}
2 changes: 2 additions & 0 deletions slinky/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod segment;

mod document;

mod dependencies_writer;
mod linker_writer;

pub use error::SlinkyError;
Expand All @@ -27,4 +28,5 @@ pub use segment::Segment;

pub use document::Document;

pub use dependencies_writer::DependenciesWriter;
pub use linker_writer::LinkerWriter;
30 changes: 4 additions & 26 deletions slinky/src/linker_writer.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
/* SPDX-FileCopyrightText: © 2024 decompals */
/* SPDX-License-Identifier: MIT */

use std::{
collections::HashSet,
fs::{self, File},
io::Write,
path::Path,
};

use crate::Settings;
use std::{collections::HashSet, io::Write, path::Path};

use crate::{file_kind::FileKind, SlinkyError};
use crate::{utils, Settings};
use crate::{FileInfo, Segment};

pub struct LinkerWriter<'a> {
Expand Down Expand Up @@ -154,24 +149,7 @@ impl<'a> LinkerWriter<'a> {
}

pub fn save_linker_script(&self, path: &Path) -> Result<(), SlinkyError> {
if let Some(parent) = path.parent() {
if let Err(e) = fs::create_dir_all(parent) {
return Err(SlinkyError::FailedDirCreate {
path: parent.to_path_buf(),
description: e.to_string(),
});
}
}

let mut f = match File::create(path) {
Ok(f) => f,
Err(e) => {
return Err(SlinkyError::FailedFileOpen {
path: path.to_path_buf(),
description: e.to_string(),
})
}
};
let mut f = utils::create_file_and_parents(path)?;

for line in &self.buffer {
if let Err(e) = writeln!(f, "{}", line) {
Expand Down
35 changes: 35 additions & 0 deletions slinky/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ pub struct Settings {

pub hardcoded_gp_value: Option<u32>,

pub d_path: Option<PathBuf>,
pub target_path: Option<PathBuf>,

pub sections_allowlist: Vec<String>,
pub sections_allowlist_extra: Vec<String>,
pub sections_denylist: Vec<String>,
Expand Down Expand Up @@ -43,6 +46,14 @@ fn settings_default_linker_symbols_style() -> LinkerSymbolsStyle {
LinkerSymbolsStyle::Splat
}

fn settings_default_d_path() -> Option<PathBuf> {
None
}

fn settings_default_target_path() -> Option<PathBuf> {
None
}

fn settings_default_hardcoded_gp_value() -> Option<u32> {
None
}
Expand Down Expand Up @@ -114,6 +125,9 @@ impl Default for Settings {
base_path: settings_default_base_path(),
linker_symbols_style: settings_default_linker_symbols_style(),

d_path: settings_default_d_path(),
target_path: settings_default_target_path(),

hardcoded_gp_value: settings_default_hardcoded_gp_value(),

sections_allowlist: settings_default_sections_allowlist(),
Expand Down Expand Up @@ -143,6 +157,11 @@ pub(crate) struct SettingsSerial {
#[serde(default)]
pub linker_symbols_style: AbsentNullable<LinkerSymbolsStyle>,

#[serde(default)]
pub d_path: AbsentNullable<PathBuf>,
#[serde(default)]
pub target_path: AbsentNullable<PathBuf>,

#[serde(default)]
pub hardcoded_gp_value: AbsentNullable<u32>,

Expand Down Expand Up @@ -189,6 +208,13 @@ impl SettingsSerial {
.hardcoded_gp_value
.get_optional_nullable("hardcoded_gp_value", settings_default_hardcoded_gp_value)?;

let d_path = self
.d_path
.get_optional_nullable("d_path", settings_default_d_path)?;
let target_path = self
.target_path
.get_optional_nullable("target_path", settings_default_target_path)?;

let sections_allowlist = self
.sections_allowlist
.get_non_null("sections_allowlist", settings_default_sections_allowlist)?;
Expand All @@ -204,6 +230,13 @@ impl SettingsSerial {
settings_default_discard_wildcard_section,
)?;

if d_path.is_some() && target_path.is_none() {
return Err(SlinkyError::MissingRequiredFieldCombo {
required: "target_path".to_string(),
other: "d_path".to_string(),
});
}

let alloc_sections = self
.alloc_sections
.get_non_null("alloc_sections", settings_default_alloc_sections)?;
Expand Down Expand Up @@ -235,6 +268,8 @@ impl SettingsSerial {
base_path,
linker_symbols_style,
hardcoded_gp_value,
d_path,
target_path,
sections_allowlist,
sections_allowlist_extra,
sections_denylist,
Expand Down
27 changes: 27 additions & 0 deletions slinky/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
/* SPDX-FileCopyrightText: © 2024 decompals */
/* SPDX-License-Identifier: MIT */

use std::{
fs::{self, File},
path::Path,
};

use crate::SlinkyError;

pub(crate) fn capitalize(s: &str) -> String {
// TODO: there must be a better way to Capitalize a string

Expand All @@ -10,3 +17,23 @@ pub(crate) fn capitalize(s: &str) -> String {

s.chars().next().expect("").to_uppercase().to_string() + &s[1..]
}

pub(crate) fn create_file_and_parents(path: &Path) -> Result<File, SlinkyError> {
// First we make the parents
if let Some(parent) = path.parent() {
if let Err(e) = fs::create_dir_all(parent) {
return Err(SlinkyError::FailedDirCreate {
path: parent.to_path_buf(),
description: e.to_string(),
});
}
}

match File::create(path) {
Ok(f) => Ok(f),
Err(e) => Err(SlinkyError::FailedFileOpen {
path: path.to_path_buf(),
description: e.to_string(),
}),
}
}
Loading

0 comments on commit 2e4e532

Please sign in to comment.