diff --git a/slinky/src/file_info.rs b/slinky/src/file_info.rs index 1456491..0ab10ca 100644 --- a/slinky/src/file_info.rs +++ b/slinky/src/file_info.rs @@ -28,6 +28,31 @@ pub struct FileInfo { // Used for groups pub files: Vec, 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)>, +} + +impl FileInfo { + pub fn new_object(p: PathBuf) -> Self { + Self { + path: p, + kind: FileKind::Object, + subfile: "".into(), + pad_amount: 0, + section: "".into(), + linker_offset_name: "".into(), + section_order: HashMap::new(), + files: Vec::new(), + dir: PathBuf::new(), + include_if_any: Vec::new(), + include_if_all: Vec::new(), + exclude_if_any: Vec::new(), + exclude_if_all: Vec::new(), + } + } } #[derive(Deserialize, PartialEq, Debug)] @@ -57,6 +82,15 @@ pub(crate) struct FileInfoSerial { pub files: AbsentNullable>, #[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>, } impl FileInfoSerial { @@ -203,6 +237,19 @@ impl FileInfoSerial { FileKind::Group => self.dir.get_non_null("dir", PathBuf::default)?, }; + 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)?; + Ok(FileInfo { path, kind, @@ -213,6 +260,10 @@ impl FileInfoSerial { section_order, files, dir, + include_if_any, + include_if_all, + exclude_if_any, + exclude_if_all, }) } } diff --git a/slinky/src/linker_writer.rs b/slinky/src/linker_writer.rs index 0e57771..6a7abca 100644 --- a/slinky/src/linker_writer.rs +++ b/slinky/src/linker_writer.rs @@ -620,6 +620,43 @@ 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)) + { + 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/partial_linker_writer.rs b/slinky/src/partial_linker_writer.rs index 2dc186c..1ffc158 100644 --- a/slinky/src/partial_linker_writer.rs +++ b/slinky/src/partial_linker_writer.rs @@ -1,12 +1,9 @@ /* SPDX-FileCopyrightText: © 2024 decompals */ /* SPDX-License-Identifier: MIT */ -use std::{ - collections::HashMap, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; -use crate::{Document, FileInfo, FileKind, LinkerWriter, Segment, SlinkyError}; +use crate::{Document, FileInfo, LinkerWriter, Segment, SlinkyError}; pub struct PartialLinkerWriter<'a> { main_writer: LinkerWriter<'a>, @@ -48,17 +45,7 @@ impl<'a> PartialLinkerWriter<'a> { p.push(&format!("{}.o", segment.name)); let mut reference_segment = segment.clone(); - reference_segment.files = vec![FileInfo { - path: p, - kind: FileKind::Object, - subfile: "".into(), - pad_amount: 0, - section: "".into(), - linker_offset_name: "".into(), - section_order: HashMap::new(), - files: Vec::new(), - dir: PathBuf::new(), - }]; + reference_segment.files = vec![FileInfo::new_object(p)]; self.main_writer.add_segment(&reference_segment)?; } diff --git a/tests/test_cases/conditional_includes.ld b/tests/test_cases/conditional_includes.ld new file mode 100644 index 0000000..6016c05 --- /dev/null +++ b/tests/test_cases/conditional_includes.ld @@ -0,0 +1,213 @@ +SECTIONS +{ + __romPos = 0x0; + + boot_ROM_START = __romPos; + boot_VRAM = ADDR(.boot); + boot_alloc_VRAM = .; + + .boot 0x80000460 : AT(boot_ROM_START) SUBALIGN(16) + { + FILL(0x00000000); + boot_TEXT_START = .; + build/us/src/boot/boot_main.o(.text*); + build/us/src/boot/file1.o(.text*); + build/us/src/boot/file3.o(.text*); + build/us/src/boot/file4.o(.text*); + build/us/src/boot/file8.o(.text*); + build/us/src/boot/file9.o(.text*); + build/us/src/boot/dmadata.o(.text*); + build/us/src/boot/file12.o(.text*); + build/us/src/boot/file15.o(.text*); + build/us/src/boot/file16.o(.text*); + build/us/src/boot/file17.o(.text*); + build/us/src/boot/util.o(.text*); + build/us/src/boot/file21.o(.text*); + build/us/src/boot/file24.o(.text*); + build/us/src/boot/file28.o(.text*); + build/us/src/boot/file29.o(.text*); + . = ALIGN(., 0x10); + boot_TEXT_END = .; + boot_TEXT_SIZE = ABSOLUTE(boot_TEXT_END - boot_TEXT_START); + + boot_DATA_START = .; + build/us/src/boot/boot_main.o(.data*); + build/us/src/boot/file1.o(.data*); + build/us/src/boot/file3.o(.data*); + build/us/src/boot/file4.o(.data*); + build/us/src/boot/file8.o(.data*); + build/us/src/boot/file9.o(.data*); + build/us/src/boot/dmadata.o(.data*); + build/us/src/boot/file12.o(.data*); + build/us/src/boot/file15.o(.data*); + build/us/src/boot/file16.o(.data*); + build/us/src/boot/file17.o(.data*); + build/us/src/boot/util.o(.data*); + build/us/src/boot/file21.o(.data*); + build/us/src/boot/file24.o(.data*); + build/us/src/boot/file28.o(.data*); + build/us/src/boot/file29.o(.data*); + . = ALIGN(., 0x10); + boot_DATA_END = .; + boot_DATA_SIZE = ABSOLUTE(boot_DATA_END - boot_DATA_START); + + boot_RODATA_START = .; + build/us/src/boot/boot_main.o(.rodata*); + build/us/src/boot/file1.o(.rodata*); + build/us/src/boot/file3.o(.rodata*); + build/us/src/boot/file4.o(.rodata*); + build/us/src/boot/file8.o(.rodata*); + build/us/src/boot/file9.o(.rodata*); + build/us/src/boot/dmadata.o(.rodata*); + build/us/src/boot/file12.o(.rodata*); + build/us/src/boot/file15.o(.rodata*); + build/us/src/boot/file16.o(.rodata*); + build/us/src/boot/file17.o(.rodata*); + build/us/src/boot/util.o(.rodata*); + build/us/src/boot/file21.o(.rodata*); + build/us/src/boot/file24.o(.rodata*); + build/us/src/boot/file28.o(.rodata*); + build/us/src/boot/file29.o(.rodata*); + . = ALIGN(., 0x10); + boot_RODATA_END = .; + boot_RODATA_SIZE = ABSOLUTE(boot_RODATA_END - boot_RODATA_START); + + boot_SDATA_START = .; + build/us/src/boot/boot_main.o(.sdata*); + build/us/src/boot/file1.o(.sdata*); + build/us/src/boot/file3.o(.sdata*); + build/us/src/boot/file4.o(.sdata*); + build/us/src/boot/file8.o(.sdata*); + build/us/src/boot/file9.o(.sdata*); + build/us/src/boot/dmadata.o(.sdata*); + build/us/src/boot/file12.o(.sdata*); + build/us/src/boot/file15.o(.sdata*); + build/us/src/boot/file16.o(.sdata*); + build/us/src/boot/file17.o(.sdata*); + build/us/src/boot/util.o(.sdata*); + build/us/src/boot/file21.o(.sdata*); + build/us/src/boot/file24.o(.sdata*); + build/us/src/boot/file28.o(.sdata*); + build/us/src/boot/file29.o(.sdata*); + . = ALIGN(., 0x10); + boot_SDATA_END = .; + boot_SDATA_SIZE = ABSOLUTE(boot_SDATA_END - boot_SDATA_START); + } + + boot_alloc_VRAM_END = .; + boot_alloc_VRAM_SIZE = ABSOLUTE(boot_alloc_VRAM_END - boot_alloc_VRAM); + + boot_noload_VRAM = .; + + .boot.noload (NOLOAD) : SUBALIGN(16) + { + FILL(0x00000000); + boot_SBSS_START = .; + build/us/src/boot/boot_main.o(.sbss*); + build/us/src/boot/file1.o(.sbss*); + build/us/src/boot/file3.o(.sbss*); + build/us/src/boot/file4.o(.sbss*); + build/us/src/boot/file8.o(.sbss*); + build/us/src/boot/file9.o(.sbss*); + build/us/src/boot/dmadata.o(.sbss*); + build/us/src/boot/file12.o(.sbss*); + build/us/src/boot/file15.o(.sbss*); + build/us/src/boot/file16.o(.sbss*); + build/us/src/boot/file17.o(.sbss*); + build/us/src/boot/util.o(.sbss*); + build/us/src/boot/file21.o(.sbss*); + build/us/src/boot/file24.o(.sbss*); + build/us/src/boot/file28.o(.sbss*); + build/us/src/boot/file29.o(.sbss*); + . = ALIGN(., 0x10); + boot_SBSS_END = .; + boot_SBSS_SIZE = ABSOLUTE(boot_SBSS_END - boot_SBSS_START); + + boot_SCOMMON_START = .; + build/us/src/boot/boot_main.o(.scommon*); + build/us/src/boot/file1.o(.scommon*); + build/us/src/boot/file3.o(.scommon*); + build/us/src/boot/file4.o(.scommon*); + build/us/src/boot/file8.o(.scommon*); + build/us/src/boot/file9.o(.scommon*); + build/us/src/boot/dmadata.o(.scommon*); + build/us/src/boot/file12.o(.scommon*); + build/us/src/boot/file15.o(.scommon*); + build/us/src/boot/file16.o(.scommon*); + build/us/src/boot/file17.o(.scommon*); + build/us/src/boot/util.o(.scommon*); + build/us/src/boot/file21.o(.scommon*); + build/us/src/boot/file24.o(.scommon*); + build/us/src/boot/file28.o(.scommon*); + build/us/src/boot/file29.o(.scommon*); + . = ALIGN(., 0x10); + boot_SCOMMON_END = .; + boot_SCOMMON_SIZE = ABSOLUTE(boot_SCOMMON_END - boot_SCOMMON_START); + + boot_BSS_START = .; + build/us/src/boot/boot_main.o(.bss*); + build/us/src/boot/file1.o(.bss*); + build/us/src/boot/file3.o(.bss*); + build/us/src/boot/file4.o(.bss*); + build/us/src/boot/file8.o(.bss*); + build/us/src/boot/file9.o(.bss*); + build/us/src/boot/dmadata.o(.bss*); + build/us/src/boot/file12.o(.bss*); + build/us/src/boot/file15.o(.bss*); + build/us/src/boot/file16.o(.bss*); + build/us/src/boot/file17.o(.bss*); + build/us/src/boot/util.o(.bss*); + build/us/src/boot/file21.o(.bss*); + build/us/src/boot/file24.o(.bss*); + build/us/src/boot/file28.o(.bss*); + build/us/src/boot/file29.o(.bss*); + . = ALIGN(., 0x10); + boot_BSS_END = .; + boot_BSS_SIZE = ABSOLUTE(boot_BSS_END - boot_BSS_START); + + bootCOMMON_START = .; + build/us/src/boot/boot_main.o(COMMON*); + build/us/src/boot/file1.o(COMMON*); + build/us/src/boot/file3.o(COMMON*); + build/us/src/boot/file4.o(COMMON*); + build/us/src/boot/file8.o(COMMON*); + build/us/src/boot/file9.o(COMMON*); + build/us/src/boot/dmadata.o(COMMON*); + build/us/src/boot/file12.o(COMMON*); + build/us/src/boot/file15.o(COMMON*); + build/us/src/boot/file16.o(COMMON*); + build/us/src/boot/file17.o(COMMON*); + build/us/src/boot/util.o(COMMON*); + build/us/src/boot/file21.o(COMMON*); + build/us/src/boot/file24.o(COMMON*); + build/us/src/boot/file28.o(COMMON*); + build/us/src/boot/file29.o(COMMON*); + . = ALIGN(., 0x10); + bootCOMMON_END = .; + bootCOMMON_SIZE = ABSOLUTE(bootCOMMON_END - bootCOMMON_START); + } + + boot_noload_VRAM_END = .; + boot_noload_VRAM_SIZE = ABSOLUTE(boot_noload_VRAM_END - boot_noload_VRAM); + boot_VRAM_END = .; + boot_VRAM_SIZE = ABSOLUTE(boot_VRAM_END - boot_VRAM); + __romPos += SIZEOF(.boot); + boot_ROM_END = __romPos; + boot_ROM_SIZE = ABSOLUTE(boot_ROM_END - boot_ROM_START); + + .shstrtab 0 : + { + *(.shstrtab); + } + + /DISCARD/ : + { + *(.reginfo); + *(.MIPS.abiflags); + *(.MIPS.options); + *(.note.gnu.build-id); + *(.interp); + *(.eh_frame); + *(*); + } +} diff --git a/tests/test_cases/conditional_includes.yaml b/tests/test_cases/conditional_includes.yaml new file mode 100644 index 0000000..53a1801 --- /dev/null +++ b/tests/test_cases/conditional_includes.yaml @@ -0,0 +1,37 @@ +settings: + base_path: build/{version} + +segments: + - name: boot + fixed_vram: 0x80000460 + files: + - { path: src/boot/boot_main.o } + - { path: src/boot/file1.o, include_if_any: [[version, us], [version, jp]] } + - { path: src/boot/file2.o, include_if_any: [[version, jp]] } + - { path: src/boot/file3.o, include_if_any: [[version, us]] } + - { path: src/boot/file4.o, include_if_all: [[version, us]] } + - { path: src/boot/file5.o, include_if_all: [[version, jp]] } + - { path: src/boot/file6.o, include_if_all: [[version, us], [modding, true]] } + - { path: src/boot/file7.o, include_if_any: [[version, jp], [modding, true]] } + - { path: src/boot/file8.o, include_if_any: [[version, us], [modding, true]] } + - { path: src/boot/file9.o, include_if_any: [[version, jp], [modding, true]], include_if_all: [[version, us]] } + - { path: src/boot/dmadata.o } + - { path: src/boot/file11.o, exclude_if_any: [[version, us], [version, jp]] } + - { path: src/boot/file12.o, exclude_if_any: [[version, jp]] } + - { path: src/boot/file13.o, exclude_if_any: [[version, us]] } + - { path: src/boot/file14.o, exclude_if_all: [[version, us]] } + - { path: src/boot/file15.o, exclude_if_all: [[version, jp]] } + - { path: src/boot/file16.o, exclude_if_all: [[version, us], [modding, true]] } + - { path: src/boot/file17.o, exclude_if_any: [[version, jp], [modding, true]] } + - { path: src/boot/file18.o, exclude_if_any: [[version, us], [modding, true]] } + - { path: src/boot/file19.o, exclude_if_any: [[version, jp], [modding, true]], exclude_if_all: [[version, us]] } + - { path: src/boot/util.o } + - { path: src/boot/file21.o, include_if_any: [[version, us], [version, jp]], exclude_if_any: [[modding, true]] } + - { path: src/boot/file22.o, include_if_any: [[version, jp]], exclude_if_any: [[version, us]] } + - { path: src/boot/file23.o, include_if_any: [[version, us]], exclude_if_any: [[version, us]] } + - { path: src/boot/file24.o, include_if_all: [[version, us]], exclude_if_any: [[version, jp]] } + - { path: src/boot/file25.o, include_if_all: [[version, jp]], exclude_if_any: [[version, jp]] } + - { path: src/boot/file26.o, include_if_all: [[version, us], [modding, true]], exclude_if_any: [[version, us]] } + - { path: src/boot/file27.o, include_if_any: [[version, jp], [modding, true]], exclude_if_any: [[version, us]] } + - { path: src/boot/file28.o, include_if_any: [[version, us], [modding, true]], exclude_if_any: [[version, jp]] } + - { path: src/boot/file29.o, include_if_any: [[version, jp], [modding, true]], include_if_all: [[version, us]], exclude_if_any: [[version, jp]] }