1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use std::{
16     fs::{copy, read_link, remove_dir_all},
17     os::unix::fs::symlink,
18     process::Output,
19 };
20 
21 use anyhow::{anyhow, Context, Result};
22 use glob::glob;
23 
24 use crate::{
25     copy_dir, crate_type::diff_android_bp, most_recent_version, CompatibleVersionPair, Crate,
26     CrateCollection, Migratable, NameAndVersionMap, PseudoCrate, RepoPath, VersionMatch,
27 };
28 
29 static CUSTOMIZATIONS: &'static [&'static str] =
30     &["*.bp", "cargo_embargo.json", "patches", "METADATA", "TEST_MAPPING", "MODULE_LICENSE_*"];
31 
32 static SYMLINKS: &'static [&'static str] = &["LICENSE", "NOTICE"];
33 
34 impl<'a> CompatibleVersionPair<'a, Crate> {
copy_customizations(&self) -> Result<()>35     pub fn copy_customizations(&self) -> Result<()> {
36         let dest_dir_absolute = self.dest.staging_path().abs();
37         for pattern in CUSTOMIZATIONS {
38             let full_pattern = self.source.path().join(pattern);
39             for entry in glob(
40                 full_pattern
41                     .abs()
42                     .to_str()
43                     .ok_or(anyhow!("Failed to convert path {} to str", full_pattern))?,
44             )? {
45                 let entry = entry?;
46                 let filename = entry
47                     .file_name()
48                     .context(format!("Failed to get file name for {}", entry.display()))?
49                     .to_os_string();
50                 if entry.is_dir() {
51                     copy_dir(&entry, &dest_dir_absolute.join(filename)).context(format!(
52                         "Failed to copy {} to {}",
53                         entry.display(),
54                         self.dest.staging_path()
55                     ))?;
56                 } else {
57                     let dest_file = dest_dir_absolute.join(&filename);
58                     if dest_file.exists() {
59                         return Err(anyhow!("Destination file {} exists", dest_file.display()));
60                     }
61                     copy(&entry, dest_dir_absolute.join(filename)).context(format!(
62                         "Failed to copy {} to {}",
63                         entry.display(),
64                         dest_dir_absolute.display()
65                     ))?;
66                 }
67             }
68         }
69         for link in SYMLINKS {
70             let src_path = self.source.path().join(link);
71             if src_path.abs().is_symlink() {
72                 let dest = read_link(src_path.abs())?;
73                 if dest.exists() {
74                     return Err(anyhow!(
75                         "Can't symlink {} -> {} because destination exists",
76                         link,
77                         dest.display(),
78                     ));
79                 }
80                 symlink(dest, dest_dir_absolute.join(link))?;
81             }
82         }
83         Ok(())
84     }
diff_android_bps(&self) -> Result<Output>85     pub fn diff_android_bps(&self) -> Result<Output> {
86         diff_android_bp(
87             &self.source.android_bp().rel(),
88             &self.dest.staging_path().join(&"Android.bp").rel(),
89             &self.source.path().root(),
90         )
91         .context("Failed to diff Android.bp".to_string())
92     }
93 }
94 
migrate( source_dir: RepoPath, pseudo_crate_dir: RepoPath, ) -> Result<VersionMatch<CrateCollection>>95 pub fn migrate(
96     source_dir: RepoPath,
97     pseudo_crate_dir: RepoPath,
98 ) -> Result<VersionMatch<CrateCollection>> {
99     let mut source = CrateCollection::new(source_dir.root());
100     source.add_from(&source_dir.rel())?;
101     source.map_field_mut().retain(|_nv, krate| krate.is_crates_io());
102 
103     let pseudo_crate = PseudoCrate::new(pseudo_crate_dir);
104     if pseudo_crate.get_path().abs().exists() {
105         remove_dir_all(pseudo_crate.get_path().abs())
106             .context(format!("Failed to remove {}", pseudo_crate.get_path()))?;
107     }
108     pseudo_crate.init(
109         source
110             .filter_versions(&most_recent_version)
111             .filter(|(_nv, krate)| krate.is_migration_eligible())
112             .map(|(_nv, krate)| krate),
113     )?;
114 
115     let mut dest = CrateCollection::new(source.repo_root());
116     dest.add_from(&pseudo_crate.get_path().join(&"vendor").rel())?;
117 
118     let mut version_match = VersionMatch::new(source, dest)?;
119 
120     version_match.stage_crates()?;
121     version_match.copy_customizations()?;
122     version_match.apply_patches()?;
123     version_match.generate_android_bps()?;
124     version_match.diff_android_bps()?;
125 
126     Ok(version_match)
127 }
128