From 9af7dc7c2fb04dea92d82a09d1b6fbb7efacf8d9 Mon Sep 17 00:00:00 2001 From: angie Date: Fri, 12 Jul 2024 12:47:01 -0400 Subject: [PATCH] Implement conditional includes for segments Closes #33 --- docs/file_format/custom_options.md | 2 +- docs/file_format/file.md | 2 +- docs/file_format/segments.md | 8 + slinky/src/document.rs | 38 ++++ slinky/src/linker_writer.rs | 39 +--- slinky/src/segment.rs | 31 +++ tests/test_cases/conditional_includes.ld | 219 +++++++++++++++++++++ tests/test_cases/conditional_includes.yaml | 30 +++ 8 files changed, 333 insertions(+), 36 deletions(-) diff --git a/docs/file_format/custom_options.md b/docs/file_format/custom_options.md index e0efe7c..8388f8f 100644 --- a/docs/file_format/custom_options.md +++ b/docs/file_format/custom_options.md @@ -126,7 +126,7 @@ combinations: ### Conditional inclusion and exclusion of file entries Custom options can be used to conditionally include or conditionally exclude -certain files. +certain [files](file.md) or [segments](segments.md). Check the [`include_if_any`](file.md#include_if_any), [`include_if_all`](file.md#include_if_all), diff --git a/docs/file_format/file.md b/docs/file_format/file.md index 23c2173..6ae18e0 100644 --- a/docs/file_format/file.md +++ b/docs/file_format/file.md @@ -326,7 +326,7 @@ segments: dir: lib include_if_any: [[compiler, modern_gcc]] files: - - { path: libgcc.a } + - { path: libgcc.{abi}.a } ``` ### Valid values diff --git a/docs/file_format/segments.md b/docs/file_format/segments.md index 22b0005..36d927d 100644 --- a/docs/file_format/segments.md +++ b/docs/file_format/segments.md @@ -38,6 +38,7 @@ Every attribute listed is optional unless explicitly stated. - [Example](#example-6) - [Valid values](#valid-values-5) - [Default value](#default-value-4) + - [`include_if_any`, `include_if_all`, `exclude_if_any` and `exclude_if_all`](#include_if_any-include_if_all-exclude_if_any-and-exclude_if_all) - [`alloc_sections`](#alloc_sections) - [Example](#example-7) - [Valid values](#valid-values-6) @@ -237,6 +238,13 @@ Any valid path. Empty path. +## `include_if_any`, `include_if_all`, `exclude_if_any` and `exclude_if_all` + +These fields allow to conditionally include or exclude a given segment depending +on the current [custom options](custom_options.md). + +Their syntax is the same as their [`file`](file.md#include_if_any) counterparts. + ## `alloc_sections` List of allocatable sections (the ones that take ROM space) for this specific diff --git a/slinky/src/document.rs b/slinky/src/document.rs index a61f450..03275c5 100644 --- a/slinky/src/document.rs +++ b/slinky/src/document.rs @@ -117,6 +117,44 @@ impl Document { Ok(new_path) } + + pub(crate) fn should_emit_entry(&self, exclude_if_any: &[(String, String)], exclude_if_all: &[(String, String)], include_if_any: &[(String, String)], include_if_all: &[(String, String)]) -> bool { + if exclude_if_any + .iter() + .any(|(key, value)| self.custom_options.get(key) == Some(value)) + { + return false; + } + + if !exclude_if_all.is_empty() + && exclude_if_all + .iter() + .all(|(key, value)| self.custom_options.get(key) == Some(value)) + { + return false; + } + + if !include_if_any.is_empty() || !include_if_all.is_empty() { + // If neither include fields match the options then we do not emit this entry + + let mut exit = false; + if !include_if_any.is_empty() { + exit = !include_if_any + .iter() + .any(|(key, value)| self.custom_options.get(key) == Some(value)); + } + if (exit || include_if_any.is_empty()) && !include_if_all.is_empty() { + exit = !include_if_all + .iter() + .all(|(key, value)| self.custom_options.get(key) == Some(value)); + } + if exit { + return false; + } + } + + true + } } #[derive(Deserialize, PartialEq, Debug)] diff --git a/slinky/src/linker_writer.rs b/slinky/src/linker_writer.rs index 6a7abca..d2dfc36 100644 --- a/slinky/src/linker_writer.rs +++ b/slinky/src/linker_writer.rs @@ -175,6 +175,10 @@ impl<'a> LinkerWriter<'a> { } pub fn add_segment(&mut self, segment: &Segment) -> Result<(), SlinkyError> { + if !self.d.should_emit_entry(&segment.exclude_if_any, &segment.exclude_if_all, &segment.include_if_any, &segment.include_if_all) { + return Ok(()); + } + assert!(!self.single_segment); let style = &self.d.settings.linker_symbols_style; @@ -620,43 +624,10 @@ impl LinkerWriter<'_> { section: &str, base_path: &Path, ) -> Result<(), SlinkyError> { - if file - .exclude_if_any - .iter() - .any(|(key, value)| self.d.custom_options.get(key) == Some(value)) - { - return Ok(()); - } - if !file.exclude_if_all.is_empty() - && file - .exclude_if_all - .iter() - .all(|(key, value)| self.d.custom_options.get(key) == Some(value)) - { + if !self.d.should_emit_entry(&file.exclude_if_any, &file.exclude_if_all, &file.include_if_any, &file.include_if_all) { return Ok(()); } - if !file.include_if_any.is_empty() || !file.include_if_all.is_empty() { - // If neither include fields match the options then we do not emit this entry - - let mut exit = false; - if !file.include_if_any.is_empty() { - exit = !file - .include_if_any - .iter() - .any(|(key, value)| self.d.custom_options.get(key) == Some(value)); - } - if (exit || file.include_if_any.is_empty()) && !file.include_if_all.is_empty() { - exit = !file - .include_if_all - .iter() - .all(|(key, value)| self.d.custom_options.get(key) == Some(value)); - } - if exit { - return Ok(()); - } - } - let style = &self.d.settings.linker_symbols_style; let wildcard = if segment.wildcard_sections { "*" } else { "" }; diff --git a/slinky/src/segment.rs b/slinky/src/segment.rs index b12b754..813846e 100644 --- a/slinky/src/segment.rs +++ b/slinky/src/segment.rs @@ -38,6 +38,11 @@ pub struct Segment { /// Used as a prefix for all the files emitted for this Segment. pub dir: PathBuf, + pub include_if_any: Vec<(String, String)>, + pub include_if_all: Vec<(String, String)>, + pub exclude_if_any: Vec<(String, String)>, + pub exclude_if_all: Vec<(String, String)>, + // The default value of the following members come from Settings pub alloc_sections: Vec, pub noload_sections: Vec, @@ -72,6 +77,15 @@ pub(crate) struct SegmentSerial { #[serde(default)] pub dir: AbsentNullable, + #[serde(default)] + pub include_if_any: AbsentNullable>, + #[serde(default)] + pub include_if_all: AbsentNullable>, + #[serde(default)] + pub exclude_if_any: AbsentNullable>, + #[serde(default)] + pub exclude_if_all: AbsentNullable>, + // The default of the following come from Options #[serde(default)] pub alloc_sections: AbsentNullable>, @@ -172,6 +186,19 @@ impl SegmentSerial { let dir = self.dir.get_non_null("dir", PathBuf::new)?; + let include_if_any = self + .include_if_any + .get_non_null("include_if_any", Vec::new)?; + let include_if_all = self + .include_if_all + .get_non_null("include_if_all", Vec::new)?; + let exclude_if_any = self + .exclude_if_any + .get_non_null("exclude_if_any", Vec::new)?; + let exclude_if_all = self + .exclude_if_all + .get_non_null("exclude_if_all", Vec::new)?; + let alloc_sections = self .alloc_sections .get_non_null("alloc_sections", || settings.alloc_sections.clone())?; @@ -207,6 +234,10 @@ impl SegmentSerial { follows_segment, vram_class, dir, + include_if_any, + include_if_all, + exclude_if_any, + exclude_if_all, alloc_sections, noload_sections, subalign, diff --git a/tests/test_cases/conditional_includes.ld b/tests/test_cases/conditional_includes.ld index 2b001a5..664eee1 100644 --- a/tests/test_cases/conditional_includes.ld +++ b/tests/test_cases/conditional_includes.ld @@ -203,6 +203,225 @@ SECTIONS boot_ROM_END = __romPos; boot_ROM_SIZE = ABSOLUTE(boot_ROM_END - boot_ROM_START); + kanji_ROM_START = __romPos; + kanji_VRAM = ADDR(.kanji); + kanji_alloc_VRAM = .; + + .kanji 0x01000000 : AT(kanji_ROM_START) SUBALIGN(16) + { + FILL(0x00000000); + kanji_TEXT_START = .; + build/us/src/language/kanji.o(.text*); + . = ALIGN(., 0x10); + kanji_TEXT_END = .; + kanji_TEXT_SIZE = ABSOLUTE(kanji_TEXT_END - kanji_TEXT_START); + + kanji_DATA_START = .; + build/us/src/language/kanji.o(.data*); + . = ALIGN(., 0x10); + kanji_DATA_END = .; + kanji_DATA_SIZE = ABSOLUTE(kanji_DATA_END - kanji_DATA_START); + + kanji_RODATA_START = .; + build/us/src/language/kanji.o(.rodata*); + . = ALIGN(., 0x10); + kanji_RODATA_END = .; + kanji_RODATA_SIZE = ABSOLUTE(kanji_RODATA_END - kanji_RODATA_START); + + kanji_SDATA_START = .; + build/us/src/language/kanji.o(.sdata*); + . = ALIGN(., 0x10); + kanji_SDATA_END = .; + kanji_SDATA_SIZE = ABSOLUTE(kanji_SDATA_END - kanji_SDATA_START); + } + + kanji_alloc_VRAM_END = .; + kanji_alloc_VRAM_SIZE = ABSOLUTE(kanji_alloc_VRAM_END - kanji_alloc_VRAM); + + kanji_noload_VRAM = .; + + .kanji.noload (NOLOAD) : SUBALIGN(16) + { + FILL(0x00000000); + kanji_SBSS_START = .; + build/us/src/language/kanji.o(.sbss*); + . = ALIGN(., 0x10); + kanji_SBSS_END = .; + kanji_SBSS_SIZE = ABSOLUTE(kanji_SBSS_END - kanji_SBSS_START); + + kanji_SCOMMON_START = .; + build/us/src/language/kanji.o(.scommon*); + . = ALIGN(., 0x10); + kanji_SCOMMON_END = .; + kanji_SCOMMON_SIZE = ABSOLUTE(kanji_SCOMMON_END - kanji_SCOMMON_START); + + kanji_BSS_START = .; + build/us/src/language/kanji.o(.bss*); + . = ALIGN(., 0x10); + kanji_BSS_END = .; + kanji_BSS_SIZE = ABSOLUTE(kanji_BSS_END - kanji_BSS_START); + + kanjiCOMMON_START = .; + build/us/src/language/kanji.o(COMMON*); + . = ALIGN(., 0x10); + kanjiCOMMON_END = .; + kanjiCOMMON_SIZE = ABSOLUTE(kanjiCOMMON_END - kanjiCOMMON_START); + } + + kanji_noload_VRAM_END = .; + kanji_noload_VRAM_SIZE = ABSOLUTE(kanji_noload_VRAM_END - kanji_noload_VRAM); + kanji_VRAM_END = .; + kanji_VRAM_SIZE = ABSOLUTE(kanji_VRAM_END - kanji_VRAM); + __romPos += SIZEOF(.kanji); + kanji_ROM_END = __romPos; + kanji_ROM_SIZE = ABSOLUTE(kanji_ROM_END - kanji_ROM_START); + + latin_alphabet_ROM_START = __romPos; + latin_alphabet_VRAM = ADDR(.latin_alphabet); + latin_alphabet_alloc_VRAM = .; + + .latin_alphabet 0x01000000 : AT(latin_alphabet_ROM_START) SUBALIGN(16) + { + FILL(0x00000000); + latin_alphabet_TEXT_START = .; + build/us/src/language/latin_alphabet.o(.text*); + . = ALIGN(., 0x10); + latin_alphabet_TEXT_END = .; + latin_alphabet_TEXT_SIZE = ABSOLUTE(latin_alphabet_TEXT_END - latin_alphabet_TEXT_START); + + latin_alphabet_DATA_START = .; + build/us/src/language/latin_alphabet.o(.data*); + . = ALIGN(., 0x10); + latin_alphabet_DATA_END = .; + latin_alphabet_DATA_SIZE = ABSOLUTE(latin_alphabet_DATA_END - latin_alphabet_DATA_START); + + latin_alphabet_RODATA_START = .; + build/us/src/language/latin_alphabet.o(.rodata*); + . = ALIGN(., 0x10); + latin_alphabet_RODATA_END = .; + latin_alphabet_RODATA_SIZE = ABSOLUTE(latin_alphabet_RODATA_END - latin_alphabet_RODATA_START); + + latin_alphabet_SDATA_START = .; + build/us/src/language/latin_alphabet.o(.sdata*); + . = ALIGN(., 0x10); + latin_alphabet_SDATA_END = .; + latin_alphabet_SDATA_SIZE = ABSOLUTE(latin_alphabet_SDATA_END - latin_alphabet_SDATA_START); + } + + latin_alphabet_alloc_VRAM_END = .; + latin_alphabet_alloc_VRAM_SIZE = ABSOLUTE(latin_alphabet_alloc_VRAM_END - latin_alphabet_alloc_VRAM); + + latin_alphabet_noload_VRAM = .; + + .latin_alphabet.noload (NOLOAD) : SUBALIGN(16) + { + FILL(0x00000000); + latin_alphabet_SBSS_START = .; + build/us/src/language/latin_alphabet.o(.sbss*); + . = ALIGN(., 0x10); + latin_alphabet_SBSS_END = .; + latin_alphabet_SBSS_SIZE = ABSOLUTE(latin_alphabet_SBSS_END - latin_alphabet_SBSS_START); + + latin_alphabet_SCOMMON_START = .; + build/us/src/language/latin_alphabet.o(.scommon*); + . = ALIGN(., 0x10); + latin_alphabet_SCOMMON_END = .; + latin_alphabet_SCOMMON_SIZE = ABSOLUTE(latin_alphabet_SCOMMON_END - latin_alphabet_SCOMMON_START); + + latin_alphabet_BSS_START = .; + build/us/src/language/latin_alphabet.o(.bss*); + . = ALIGN(., 0x10); + latin_alphabet_BSS_END = .; + latin_alphabet_BSS_SIZE = ABSOLUTE(latin_alphabet_BSS_END - latin_alphabet_BSS_START); + + latin_alphabetCOMMON_START = .; + build/us/src/language/latin_alphabet.o(COMMON*); + . = ALIGN(., 0x10); + latin_alphabetCOMMON_END = .; + latin_alphabetCOMMON_SIZE = ABSOLUTE(latin_alphabetCOMMON_END - latin_alphabetCOMMON_START); + } + + latin_alphabet_noload_VRAM_END = .; + latin_alphabet_noload_VRAM_SIZE = ABSOLUTE(latin_alphabet_noload_VRAM_END - latin_alphabet_noload_VRAM); + latin_alphabet_VRAM_END = .; + latin_alphabet_VRAM_SIZE = ABSOLUTE(latin_alphabet_VRAM_END - latin_alphabet_VRAM); + __romPos += SIZEOF(.latin_alphabet); + latin_alphabet_ROM_END = __romPos; + latin_alphabet_ROM_SIZE = ABSOLUTE(latin_alphabet_ROM_END - latin_alphabet_ROM_START); + + player_model_ROM_START = __romPos; + player_model_VRAM = ADDR(.player_model); + player_model_alloc_VRAM = .; + + .player_model 0x05000000 : AT(player_model_ROM_START) SUBALIGN(16) + { + FILL(0x00000000); + player_model_TEXT_START = .; + build/us/src/assets/player_model.o(.text*); + . = ALIGN(., 0x10); + player_model_TEXT_END = .; + player_model_TEXT_SIZE = ABSOLUTE(player_model_TEXT_END - player_model_TEXT_START); + + player_model_DATA_START = .; + build/us/src/assets/player_model.o(.data*); + . = ALIGN(., 0x10); + player_model_DATA_END = .; + player_model_DATA_SIZE = ABSOLUTE(player_model_DATA_END - player_model_DATA_START); + + player_model_RODATA_START = .; + build/us/src/assets/player_model.o(.rodata*); + . = ALIGN(., 0x10); + player_model_RODATA_END = .; + player_model_RODATA_SIZE = ABSOLUTE(player_model_RODATA_END - player_model_RODATA_START); + + player_model_SDATA_START = .; + build/us/src/assets/player_model.o(.sdata*); + . = ALIGN(., 0x10); + player_model_SDATA_END = .; + player_model_SDATA_SIZE = ABSOLUTE(player_model_SDATA_END - player_model_SDATA_START); + } + + player_model_alloc_VRAM_END = .; + player_model_alloc_VRAM_SIZE = ABSOLUTE(player_model_alloc_VRAM_END - player_model_alloc_VRAM); + + player_model_noload_VRAM = .; + + .player_model.noload (NOLOAD) : SUBALIGN(16) + { + FILL(0x00000000); + player_model_SBSS_START = .; + build/us/src/assets/player_model.o(.sbss*); + . = ALIGN(., 0x10); + player_model_SBSS_END = .; + player_model_SBSS_SIZE = ABSOLUTE(player_model_SBSS_END - player_model_SBSS_START); + + player_model_SCOMMON_START = .; + build/us/src/assets/player_model.o(.scommon*); + . = ALIGN(., 0x10); + player_model_SCOMMON_END = .; + player_model_SCOMMON_SIZE = ABSOLUTE(player_model_SCOMMON_END - player_model_SCOMMON_START); + + player_model_BSS_START = .; + build/us/src/assets/player_model.o(.bss*); + . = ALIGN(., 0x10); + player_model_BSS_END = .; + player_model_BSS_SIZE = ABSOLUTE(player_model_BSS_END - player_model_BSS_START); + + player_modelCOMMON_START = .; + build/us/src/assets/player_model.o(COMMON*); + . = ALIGN(., 0x10); + player_modelCOMMON_END = .; + player_modelCOMMON_SIZE = ABSOLUTE(player_modelCOMMON_END - player_modelCOMMON_START); + } + + player_model_noload_VRAM_END = .; + player_model_noload_VRAM_SIZE = ABSOLUTE(player_model_noload_VRAM_END - player_model_noload_VRAM); + player_model_VRAM_END = .; + player_model_VRAM_SIZE = ABSOLUTE(player_model_VRAM_END - player_model_VRAM); + __romPos += SIZEOF(.player_model); + player_model_ROM_END = __romPos; + player_model_ROM_SIZE = ABSOLUTE(player_model_ROM_END - player_model_ROM_START); + .shstrtab 0 : { *(.shstrtab); diff --git a/tests/test_cases/conditional_includes.yaml b/tests/test_cases/conditional_includes.yaml index e9a4540..63c7beb 100644 --- a/tests/test_cases/conditional_includes.yaml +++ b/tests/test_cases/conditional_includes.yaml @@ -51,3 +51,33 @@ segments: files: - { path: libgcc.a } + # This segment has a different order on the ROM depending on the version + - name: player_model + fixed_vram: 0x05000000 + include_if_any: [[version, jp]] + files: + - { path: src/assets/player_model.o } + + - name: kanji + fixed_vram: 0x01000000 + include_if_any: [[version, jp], [version, us]] + files: + - { path: src/language/kanji.o } + + - name: latin_alphabet + fixed_vram: 0x01000000 + include_if_any: [[version, us], [version, eu], [version, fr], [version, gr], [version, es]] + files: + - { path: src/language/latin_alphabet.o } + + - name: alphabet_extended + fixed_vram: 0x01000000 + include_if_all: [[modding, true], [version, ru]] + files: + - { path: src/language/alphabet_extended.o } + + - name: player_model + fixed_vram: 0x05000000 + include_if_any: [[version, us]] + files: + - { path: src/assets/player_model.o }