diff --git a/src/packaging.rs b/src/packaging.rs index 47cff1baf..a17a11984 100644 --- a/src/packaging.rs +++ b/src/packaging.rs @@ -98,65 +98,90 @@ fn copy_license_files( output: &Output, tmp_dir_path: &Path, ) -> Result>, PackagingError> { + // Early return if no license files are specified if output.recipe.about().license_file.is_empty() { - Ok(None) - } else { - let licenses_folder = tmp_dir_path.join("info/licenses/"); - fs::create_dir_all(&licenses_folder)?; + return Ok(None); + } - let copy_dir = copy_dir::CopyDir::new( + // Create licenses directory + let licenses_folder = tmp_dir_path.join("info/licenses/"); + fs::create_dir_all(&licenses_folder)?; + + // Define source directories to search for license files + let sources = [ + ( + &output.build_configuration.directories.host_prefix, + "prefix directory", + false, + ), + ( &output.build_configuration.directories.work_dir, - &licenses_folder, - ) - .with_globvec(&output.recipe.about().license_file) - .use_gitignore(false) - .run()?; + "source directory", + true, + ), + ( + &output.build_configuration.directories.recipe_dir, + "recipe directory", + true, + ), + ]; - let copied_files_work_dir = copy_dir.copied_paths(); - let any_include_matched_recipe_dir = copy_dir.any_include_glob_matched(); + let license_globs = &output.recipe.about().license_file; + let mut all_copied_files = HashSet::new(); + let mut any_glob_matched = false; + let mut copied_files_by_source = Vec::with_capacity(sources.len()); - let copy_dir = copy_dir::CopyDir::new( - &output.build_configuration.directories.recipe_dir, - &licenses_folder, - ) - .with_globvec(&output.recipe.about().license_file) - .use_gitignore(false) - .overwrite(true) - .run()?; - - let copied_files_recipe_dir = copy_dir.copied_paths(); - let any_include_matched_work_dir = copy_dir.any_include_glob_matched(); - - // if a file was copied from the recipe dir, and the work dir, we should - // issue a warning - for file in copied_files_recipe_dir { - if copied_files_work_dir.contains(file) { - let warn_str = format!( - "License file from source directory was overwritten by license file from recipe folder ({})", - file.display() - ); - tracing::warn!(warn_str); - output.record_warning(&warn_str); + // Copy license files from each source directory + for (source_dir, source_name, overwrite) in sources { + let copy_dir = copy_dir::CopyDir::new(source_dir, &licenses_folder) + .with_globvec(license_globs) + .use_gitignore(false) + .overwrite(overwrite); + + let result = copy_dir.run()?; + + let copied_files = result.copied_paths().to_vec(); + any_glob_matched |= result.any_include_glob_matched(); + + // Add all copied files to our result set + all_copied_files.extend(copied_files.iter().map(PathBuf::from)); + + copied_files_by_source.push((source_name, copied_files)); + } + + // Check for and warn about file conflicts + for i in 0..copied_files_by_source.len() { + for j in i + 1..copied_files_by_source.len() { + let (source_i_name, files_i) = &copied_files_by_source[i]; + let (source_j_name, files_j) = &copied_files_by_source[j]; + + for file in files_i { + if files_j.contains(file) { + let warn_str = format!( + "License file '{}' from {} was overwritten by license file from {}", + file.file_name().unwrap_or_default().to_string_lossy(), + source_i_name, + source_j_name + ); + tracing::warn!(warn_str); + output.record_warning(&warn_str); + } } } + } - let copied_files = copied_files_recipe_dir - .iter() - .chain(copied_files_work_dir) - .map(PathBuf::from) - .collect::>(); - - if !any_include_matched_work_dir && !any_include_matched_recipe_dir { - let warn_str = "No include glob matched for copying license files"; - tracing::warn!(warn_str); - output.record_warning(warn_str); - } + // Warn if no globs matched + if !any_glob_matched { + let warn_str = "No include glob matched for copying license files"; + tracing::warn!(warn_str); + output.record_warning(warn_str); + } - if copied_files.is_empty() { - Err(PackagingError::LicensesNotFound) - } else { - Ok(Some(copied_files)) - } + // Return error if no files were copied + if all_copied_files.is_empty() { + Err(PackagingError::LicensesNotFound) + } else { + Ok(Some(all_copied_files)) } } diff --git a/test-data/recipes/double_license/recipe.yaml b/test-data/recipes/double_license/recipe.yaml index 2d601850e..f912cbd81 100644 --- a/test-data/recipes/double_license/recipe.yaml +++ b/test-data/recipes/double_license/recipe.yaml @@ -8,6 +8,20 @@ source: build: number: 0 + script: + - if: unix + then: + - mkdir -p $PREFIX/license-folder/ + - echo "prefix-license" > $PREFIX/license-folder/prefix-license.txt + - echo "source-license" > $SRC_DIR/source-license.txt + else: + - mkdir -p %PREFIX%\license-folder\ + - echo "prefix-license" > %PREFIX%\license-folder\prefix-license.txt + - echo "source-license" > %SRC_DIR%\source-license.txt about: - license_file: license.txt + license_file: + - license.txt + - recipe_license.txt + - license-folder/prefix-license.txt + - source-license.txt diff --git a/test/end-to-end/test_simple.py b/test/end-to-end/test_simple.py index 46a5ff475..9e59c63be 100644 --- a/test/end-to-end/test_simple.py +++ b/test/end-to-end/test_simple.py @@ -852,7 +852,13 @@ def test_double_license(rattler_build: RattlerBuild, recipes: Path, tmp_path: Pa path_to_recipe = recipes / "double_license" args = rattler_build.build_args(path_to_recipe, tmp_path) output = rattler_build(*args, stderr=STDOUT) - assert "warning License file from source directory was overwritten" in output + assert "warning License file 'license.txt' from work directory was overwritten by license file from recipe directory" in output + + pkg = get_extracted_package(tmp_path, "double_license") + assert (pkg / "info/licenses/license.txt").exists() + assert (pkg / "info/licenses/recipe_license.txt").exists() + assert (pkg / "info/licenses/prefix-license.txt").exists() + assert (pkg / "info/licenses/source-license.txt").exists() @pytest.mark.skipif(