Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- The `config repositories add` command now has a `--unchecked` flag, which can be used to skip repository checks.
- The `--pause-build` flag to the `install` command, to pause the build after build script execution.
- The `switch-dependency` command, which can be used to switch dependencies of a package.
- The `license_exclude` field to `sources` in the metadata, which can be used to exclude certain paths from automatic license file detection.
- The `license_include` field to `sources` in the metadata, which can be used to include certain files in the license file copying.

### Changes
- Change display of true and false values in the `info` and `search` commands.
Expand Down
2 changes: 2 additions & 0 deletions docs/metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ The targets.toml file can contain one or multiple sources, specified in the foll
| `size` | Defines the size of the source archive in bytes. |
| `mirrors` | Defines a list of mirrors which could be used to download the sourcecode if the original url is unavailable. |
| `skip_unpack` | True to skip the unpack step and just download the source file, false to use the build in unpack. |
| `license_exclude` | A list of paths to skip when doing automatic license file detection. `*` can be used to skip detection entirely. |
| `license_include` | A list of files that need to be copied to the package license file directory. These files are copied before the automatic detection. |
| `apply_patches_in` | Defines the directory that should be used to apply all patches in. |
| `patches` | A list of patches to apply to the source. See patches section below. |

Expand Down
66 changes: 63 additions & 3 deletions src/builder/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ impl<'a> Builder<'a> {

// Copy license files
let license_directory = destination_dir.as_ref().join("share").join("licenses").join(&install_meta.package_metadata.name);
self.copy_license_files(build_directory.path(), &license_directory)?;
self.copy_license_files(build_directory.path(), &license_directory, source)?;

// Patch binaries
BinaryPatcher::new(self.config).patch_binaries_in(destination_dir.as_ref().to_path_buf(), &package_id, installed_dependencies)?;
Expand Down Expand Up @@ -355,7 +355,18 @@ impl<'a> Builder<'a> {
/// Copies license files from the original source into the destination directory.
/// Does a breadth-first search from the build directory and stops when it finds license files.
/// Only traverse to depth 2, to prevent detecting third party license files.
fn copy_license_files(&self, build_directory: &Path, destination_dir: &Path) -> Result<()> {
fn copy_license_files(&self, build_directory: &Path, destination_dir: &Path, source: &Source) -> Result<()> {
Comment thread
BraveChicken1 marked this conversation as resolved.
Comment thread
Max13245 marked this conversation as resolved.
// Copy all include paths first
self.copy_include_license_files(build_directory, destination_dir, source)?;

// Skip copying entirely if exclude `*` is specified
if source.license_exclude.iter().any(|x| x == "*") {
debug!("Skipping license file copying");
return Ok(());
}

let exclude_paths: Vec<_> = source.license_exclude.iter().map(|x| build_directory.join(x)).collect();

let mut queue = VecDeque::from([(0, build_directory.to_path_buf())]);
while let Some((depth, item)) = queue.pop_front() {
let mut found_files = false;
Expand All @@ -364,6 +375,11 @@ impl<'a> Builder<'a> {
for entry in fs::read_dir(&item).err_with_path("read", &item)? {
let entry = entry.err_with_path("iterate", &item)?;

// Skip paths that should be excluded
if exclude_paths.contains(&entry.path()) {
continue;
}

let metadata = entry.metadata().err_with_path("read metadata of", entry.path())?;

// If the entry is a directory, add it to the queue
Expand All @@ -389,13 +405,23 @@ impl<'a> Builder<'a> {
}

found_files = true;
debug!(
"Found license file at '{}'",
entry.path().strip_prefix(build_directory).unwrap_or(build_directory).display()
);

// Check if file was already copied (for example using `license_include`)
let destination_path = destination_dir.join(entry.file_name());
if destination_path.exists() {
debug!("License file with same name is already copied, skipping file");
}

// Create destination directory if it does not exist
if !destination_dir.exists() {
fs::create_dir_all(destination_dir).err_with_path("create dirs", destination_dir)?;
}

fs::copy(entry.path(), destination_dir.join(entry.file_name())).err_with_path("copy", entry.path())?;
fs::copy(entry.path(), destination_path).err_with_path("copy", entry.path())?;
break;
}
}
Expand All @@ -410,4 +436,38 @@ impl<'a> Builder<'a> {
debug!("Unable to find license files for package");
Ok(())
}

/// Copies license files that are listed as `license_include` into the destination directory.
fn copy_include_license_files(&self, build_directory: &Path, destination_dir: &Path, source: &Source) -> Result<()> {
for include_path_str in &source.license_include {
let include_path = build_directory.join(include_path_str);

// Skip if the path does not exist
if !include_path.exists() {
warning!("Specified license file include path '{include_path_str}' does not exist");
continue;
}

// Check if the path is a file
let metadata = fs::metadata(&include_path).err_with_path("read metadata", &include_path)?;
if !metadata.is_file() {
warning!("Specified license file include path '{include_path_str}' is not a file");
continue;
}

let Some(file_name) = include_path.file_name() else {
warning!("Specified license file include path '{include_path_str}' is not a valid path");
continue;
};

// Create destination directory if it does not exist
if !destination_dir.exists() {
fs::create_dir_all(destination_dir).err_with_path("create dirs", destination_dir)?;
}

fs::copy(&include_path, destination_dir.join(file_name)).err_with_path("copy", include_path)?;
}

Ok(())
}
}
2 changes: 2 additions & 0 deletions src/register/package_register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,8 @@ pub mod tests {
size: FileSize(0),
mirrors: Vec::new(),
skip_unpack: false,
license_exclude: Vec::new(),
license_include: Vec::new(),
apply_patches_in: None,
patches: HashMap::new(),
}),
Expand Down
6 changes: 6 additions & 0 deletions src/repositories/types/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ pub struct Source {

#[serde(default, skip_serializing_if = "<&bool>::not")]
pub skip_unpack: bool,

#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub license_exclude: Vec<String>,

#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub license_include: Vec<String>,
pub apply_patches_in: Option<String>,

#[serde(default, deserialize_with = "Source::deserialize_patches", skip_serializing_if = "HashMap::is_empty")]
Expand Down