xref: /aosp_15_r20/development/tools/external_crates/crate_tool/src/license.rs (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1 // Copyright (C) 2024 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     collections::BTreeMap,
17     fs::{remove_file, write},
18     path::Path,
19     sync::LazyLock,
20 };
21 
22 use anyhow::{anyhow, Result};
23 use glob::glob;
24 use google_metadata::metadata::LicenseType;
25 use license_checker::LicenseState;
26 use spdx::{LicenseReq, Licensee};
27 
28 /// Update MODULE_LICENSE_* files in a directory based on the applicable licenses.
29 /// These files are typically empty, and their name indicates the type of license that
30 /// applies to the code, for example MODULE_LICENSE_APACHE2.
update_module_license_files(path: &impl AsRef<Path>, licenses: &LicenseState) -> Result<()>31 pub fn update_module_license_files(path: &impl AsRef<Path>, licenses: &LicenseState) -> Result<()> {
32     let path = path.as_ref();
33     for old_module_license_file in glob(
34         path.join("MODULE_LICENSE*").to_str().ok_or(anyhow!("Failed to convert path to string"))?,
35     )? {
36         remove_file(old_module_license_file?)?;
37     }
38     for license in licenses.satisfied.keys().chain(&licenses.unsatisfied) {
39         if let Some(mod_lic) = MODULE_LICENSE_FILES.get(license) {
40             write(path.join(mod_lic), "")?; // Write an empty file. Essentially "touch".
41         }
42     }
43     Ok(())
44 }
45 
discriminant(lt: LicenseType) -> u846 fn discriminant(lt: LicenseType) -> u8 {
47     // Smaller --> more restricted
48     // Larger --> less restricted
49     match lt {
50         LicenseType::UNKNOWN => 0,
51         LicenseType::BY_EXCEPTION_ONLY => 1,
52         LicenseType::RESTRICTED => 2,
53         LicenseType::RESTRICTED_IF_STATICALLY_LINKED => 3,
54         LicenseType::RECIPROCAL => 4,
55         LicenseType::NOTICE => 5,
56         LicenseType::PERMISSIVE => 6,
57         LicenseType::UNENCUMBERED => 7,
58     }
59 }
60 
most_restrictive_type(licenses: &LicenseState) -> LicenseType61 pub fn most_restrictive_type(licenses: &LicenseState) -> LicenseType {
62     licenses
63         .satisfied
64         .keys()
65         .chain(&licenses.unsatisfied)
66         .map(|req| LICENSE_TYPES.get(req).cloned().unwrap_or(LicenseType::UNKNOWN))
67         .min_by(|a, b| discriminant(*a).cmp(&discriminant(*b)))
68         .unwrap_or(LicenseType::UNKNOWN)
69 }
70 
71 static MODULE_LICENSE_FILES: LazyLock<BTreeMap<LicenseReq, &'static str>> = LazyLock::new(|| {
72     vec![
73         ("Apache-2.0", "MODULE_LICENSE_APACHE2"),
74         ("MIT", "MODULE_LICENSE_MIT"),
75         ("BSD-3-Clause", "MODULE_LICENSE_BSD"),
76         ("BSD-2-Clause", "MODULE_LICENSE_BSD"),
77         ("ISC", "MODULE_LICENSE_ISC"),
78         ("MPL-2.0", "MODULE_LICENSE_MPL"),
79         ("0BSD", "MODULE_LICENSE_PERMISSIVE"),
80         ("Unlicense", "MODULE_LICENSE_PERMISSIVE"),
81         ("Zlib", "MODULE_LICENSE_ZLIB"),
82         ("Unicode-DFS-2016", "MODULE_LICENSE_UNICODE"),
83         ("NCSA", "MODULE_LICENSE_NCSA"),
84         ("OpenSSL", "MODULE_LICENSE_OPENSSL"),
85     ]
86     .into_iter()
87     .map(|l| (Licensee::parse(l.0).unwrap().into_req(), l.1))
88     .collect()
89 });
90 static LICENSE_TYPES: LazyLock<BTreeMap<LicenseReq, LicenseType>> = LazyLock::new(|| {
91     vec![
92         ("Apache-2.0", LicenseType::NOTICE),
93         ("MIT", LicenseType::NOTICE),
94         ("BSD-3-Clause", LicenseType::NOTICE),
95         ("BSD-2-Clause", LicenseType::NOTICE),
96         ("ISC", LicenseType::NOTICE),
97         ("MPL-2.0", LicenseType::RECIPROCAL),
98         ("0BSD", LicenseType::PERMISSIVE),
99         ("Unlicense", LicenseType::PERMISSIVE),
100         ("Zlib", LicenseType::NOTICE),
101         ("Unicode-DFS-2016", LicenseType::NOTICE),
102         ("NCSA", LicenseType::NOTICE),
103         ("OpenSSL", LicenseType::NOTICE),
104     ]
105     .into_iter()
106     .map(|l| (Licensee::parse(l.0).unwrap().into_req(), l.1))
107     .collect()
108 });
109