diff --git a/get_license/check_license.py b/get_license/check_license.py new file mode 100644 index 0000000000000000000000000000000000000000..49d9880a1cb58d13c75d255308ac0afbf6d42e57 --- /dev/null +++ b/get_license/check_license.py @@ -0,0 +1,274 @@ +#! /usr/bin/env python +# coding=utf-8 +# ****************************************************************************** +# Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved. +# licensed under the Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# http://license.coscl.org.cn/MulanPSL2 +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR +# PURPOSE. +# See the Mulan PSL v2 for more details. +# Author: +# Create: 2020-10-13 +# ******************************************************************************/ +""" +check license for source package +""" +import os +import re +import chardet +import argparse +import yaml + +from get_license.spec import Spec, replace_macros +import logging + +log_check = logging.getLogger() + + +class PkgLicense(object): + """ + 解析获取软件包中源码、spec中的license + 进行白名单校验、一致性检查 + """ + + LICENSE_FILE_TARGET = ["apache-2.0", + "artistic", + "artistic.txt", + "libcurllicense", + "gpl.txt", + "gpl2.txt", + "gplv2.txt", + "lgpl.txt", + "notice", + "about_bsd.txt", + "mit", + "pom.xml", + "meta.yml", + "pkg-info"] + + LICENSE_TARGET_PAT = re.compile( + r"^(copying)|(copyright)|(copyrights)|(licenses)|(licen[cs]e)(\.(txt|xml))?$") + + def __init__(self): + self.license_list = {} + self.full_name_map = {} + + @staticmethod + def load_licenses_config(filename): + white_black_list = {} + translations = {} + if not os.path.exists(filename): + log_check.warning("not found License config: %s", filename) + return white_black_list, translations + data = {} + with open(filename, "r") as f: + try: + data = yaml.safe_load(f) + except yaml.YAMLError as e: + log_check.exception("yaml load error: %s", str(e)) + return white_black_list, translations + PkgLicense.parse_tag_license(data["Software Licenses"]["Bad Licenses"], + "black", + white_black_list, + translations) + PkgLicense.parse_tag_license(data["Software Licenses"]["Good Licenses"], + "white", + white_black_list, + translations) + PkgLicense.parse_tag_license(data["Software Licenses"]["Need Review Licenses"], + "need review", + white_black_list, + translations) + return white_black_list, translations + + @staticmethod + def parse_tag_license(licenses, tag, white_black_list, translations): + for lic in licenses: + if lic["identifier"] not in white_black_list: + white_black_list[lic["identifier"]] = tag + for oname in lic["alias"]: + if oname not in translations: + translations[oname] = lic["identifier"] + + @staticmethod + def check_license_safe(licenses, license_list): + """ + Check if the license is in the blacklist. + """ + result = True + for lic in licenses: + res = license_list.get(lic, "need review") + if res == "white": + pass + elif res == "black": + log_check.error("This license: %s is not safe", lic) + result = False + else: + log_check.warning("This license: %s need to be review", lic) + result = False + return result + + # 以下为从spec文件中获取license + @staticmethod + def scan_licenses_in_spec(specfile, license_translations): + """ + Find spec file and scan. If no spec file + or open file failed, the program will exit with an error. + """ + if not specfile: + return [] + s_spec = Spec.from_file(specfile) + licenses = replace_macros(s_spec.license, s_spec) + licenses_in_spec = set() + + words = PkgLicense.split_license(licenses) + for word in words: + licenses_in_spec.add(word) + result = PkgLicense._translate_license(licenses_in_spec, license_translations) + if not licenses_in_spec: + log_check.warning("full license in spec is empty") + else: + log_check.info("full license in spec: %s", ", ".join(list(licenses_in_spec))) + if not license_translations: + log_check.warning("real license in spec is empty") + else: + log_check.info("real license in spec: %s", ", ".join(result)) + return result + + @staticmethod + def split_license(licenses): + """ + 分割spec license字段的license + """ + license_set = re.split(r'\(|\)|\,|\W+[Aa][Nn][Dd]\W+|\s+-or-\s+|\s+[oO][rR]\s+|\s+/\s+', licenses) + for index in range(len(license_set)): # 去除字符串首尾空格 + license_set[index] = license_set[index].strip() + return set(filter(None, license_set)) # 去除list中空字符串 + + # 以下为从license文件中获取license + @staticmethod + def scan_licenses_in_source(srcdir, license_translations, head=True): + """ + Find LICENSE files and scan. + """ + full_license_in_file = set() + if not os.path.exists(srcdir): + log_check.error("%s not exist.", srcdir) + return full_license_in_file + with os.scandir(srcdir) as dir_iter: + for entry in dir_iter: + if not entry.name.startswith('.'): + file_name = entry.name + if os.path.islink(os.path.join(srcdir, file_name)): + continue + # print("now file: {}".format(file_name)) + if entry.is_file(): + if file_name.lower() in PkgLicense.LICENSE_FILE_TARGET \ + or PkgLicense.LICENSE_TARGET_PAT.search(file_name.lower()): + # print("scan the license target file: {}".format(file_name)) + log_check.info("scan the license target file: %s", file_name) + full_license_in_file.update( + PkgLicense.scan_licenses( + os.path.join(srcdir, file_name), + license_translations)) + else: + full_license_in_file.update( + PkgLicense.scan_licenses_in_source( + os.path.join(srcdir, file_name), + license_translations, + False)) + result = full_license_in_file + if head: + log_check.info("full license in files: %s", ", ".join(list(full_license_in_file))) + result = PkgLicense._translate_license(full_license_in_file, license_translations) + log_check.info("real license in files: %s", ", ".join(result)) + return result + + @staticmethod + def scan_licenses(copying, license_translations): + """ + Scan licenses from copying file and add to licenses_for_source_files. + if get contents failed or decode data failed, return nothing. + """ + licenses_in_file = set() + log_check.info("%d", len(license_translations)) + for word in license_translations: + if word in copying: + licenses_in_file.add(word) + log_check.info("get license({}) from file({})".format(word, copying)) + try: + with open(copying, "rb") as file_handler: + data = file_handler.read() + except FileNotFoundError: + return licenses_in_file + data = PkgLicense._auto_decode_data(data) + if not data: + return licenses_in_file + content = ' '.join(str(data).split()) + for word in license_translations: + try: + if word in content: + pattern_str = r'(^{word}$)|(^{word}(\W+).*)|(.*(\W+){word}$)|(.*(\W+){word}(\W+).*)' \ + .format(word=word) + if re.match(r'' + pattern_str + '', content): + licenses_in_file.add(word) + print("get license({}) from file({})".format(word, copying)) + log_check.info("get license({}) from file({})".format(word, copying)) + except UnicodeDecodeError as e: + log_check.exception("decode error: %s", str(e)) + return licenses_in_file + + @staticmethod + def _decode_data(data, charset): + """ + Decode the license string. return the license string or nothing. + """ + if not charset: + return "" + try: + return data.decode(charset) + except UnicodeDecodeError as ue: + log_check.exception("auto decode data error %s", str(ue)) + return "" + + @staticmethod + def _auto_decode_data(data): + return PkgLicense._decode_data(data, chardet.detect(data)['encoding']) + + @staticmethod + def _translate_license(data, license_translations): + result = set() + for e in data: + print("origin:{} after:{}".format(e, license_translations.get(e, e))) + result.add(license_translations.get(e, e)) + return list(result) + + @staticmethod + def check_licenses_is_same(licenses_for_spec, licenses_for_source_files): + """ + Check if the licenses from SPEC is the same as the licenses from LICENSE file. + if license in spec contains license in srcs, return True. if not same return False. + """ + return set(licenses_for_source_files) <= set(licenses_for_spec) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-l", "--cvelist", default="", + help="path of cve pkgs") + # input of new repo name + parser.add_argument("-o", "--old_repos", type=str, nargs='*', required=True, + help="repo list of old repo") + parser.add_argument("-n", "--new_repos", type=str, nargs='*', required=True, + help="repo list of new repo") + + parser.add_argument("-s", "--savedir", default="/tmp/update-package/pkg_rpm", + help="dir of download dir") + parser.add_argument("-d", "--downloaddir", default="/tmp/update-package/download", + help="dir of require download dir") + parser.add_argument("--skip_cve_list", action="store_true", default=False, + help="skip download pkg in cve list") + args = parser.parse_args() \ No newline at end of file diff --git a/get_license/config/Licenses.yaml b/get_license/config/Licenses.yaml new file mode 100644 index 0000000000000000000000000000000000000000..48ea42c4cdaceaf40abc714cafd87c2f7df7f1a0 --- /dev/null +++ b/get_license/config/Licenses.yaml @@ -0,0 +1,1546 @@ +Software Licenses: + Bad Licenses: + - alias: + - Abstyles License + - APREAMBL.TEX, version 1.10e + identifier: Abstyles + - alias: + - Adobe Systems Incorporated Source Code License Agreement + - Adobe Systems Incorporated(r) Source Code License Agreement + identifier: Adobe-2006 + - alias: + - Adobe Glyph List License + - Copyright (c) 1997,1998,2002,2007 Adobe Systems Incorporated + identifier: Adobe-Glyph + - alias: + - Amazon Digital Services License + - Amazon Digital Services, Inc. or its affiliates + identifier: ADSL + - alias: + - Affero General Public License v1.0 only + - AFFERO GENERAL PUBLIC LICENSE Version 1 + identifier: AGPL-1.0-only + - alias: + - Affero General Public License v1.0 or later + - AFFERO GENERAL PUBLIC LICENSE Version 1 + identifier: AGPL-1.0-or-later + - alias: + - Aladdin Free Public License + identifier: Aladdin + - alias: + - AMD's plpa_map.c License + - Copyright (c) 2006, 2007 Advanced Micro Devices, Inc. + identifier: AMDPLPA + - alias: + - Apple MIT License + - Copyright (c) 2006 by Apple Computer, Inc. + identifier: AML + - alias: + - Academy of Motion Picture Arts and Sciences BSD + - A.M.P.A.S. + identifier: AMPAS + - alias: + - ANTLR Software Rights Notice + - ANTLR 2 License + identifier: ANTLR-PD + - alias: + - Bahyph License + - Copyright (c) GMV 1991 + identifier: Bahyph + - alias: + - Barr License + - Michael Barr + identifier: Barr + - alias: + - Beerware License + - THE BEER-WARE LICENSE + identifier: Beerware + - alias: + - BitTorrent Open Source License v1.0 + - BitTorrent Open Source License Version 1.0 + identifier: BitTorrent-1.0 + - alias: + - SQLite Blessing + - here is a blessing + identifier: blessing + - alias: + - Blue Oak Model License 1.0.0 + - "http://blueoakcouncil.org/license/1.0.0" + identifier: BlueOak-1.0.0 + - alias: + - Borceux license + - Copyright 1993 Francis Borceux + identifier: Borceux + - alias: + - BSD 3-Clause No Nuclear License + - Copyright 1994-2009 Sun Microsystems, Inc. + identifier: BSD-3-Clause-No-Nuclear-License + - alias: + - BSD 3-Clause No Nuclear License 2014 + - Copyright © 2008, 2014 Oracle and/or its affiliates + identifier: BSD-3-Clause-No-Nuclear-License-2014 + - alias: + - BSD 3-Clause No Nuclear Warranty + - Copyright (c) 2003-2005 Sun Microsystems, Inc. + identifier: BSD-3-Clause-No-Nuclear-Warranty + - alias: + - BSD 3-Clause Open MPI variant + identifier: BSD-3-Clause-Open-MPI + - alias: + - BSD-4-Clause (University of California-Specific) + identifier: BSD-4-Clause-UC + - alias: + - BSD Source Code Attribution + - Copyright (c) 2011, Deusty, LLC + identifier: BSD-Source-Code + - alias: + - bzip2 and libbzip2 License v1.0.5 + - bzip2/libbzip2 version 1.0.5 + identifier: bzip2-1.0.5 + - alias: + - bzip2 and libbzip2 License v1.0.6 + - bzip2/libbzip2 version 1.0.6 + identifier: bzip2-1.0.6 + - alias: + - Caldera License + - Caldera International, Inc. + identifier: Caldera + - alias: + - Creative Commons Attribution 2.0 Generic + - Creative Commons Attribution 2.0 + identifier: CC-BY-2.0 + - alias: + - Creative Commons Attribution 2.5 Generic + - Creative Commons Attribution 2.0 + identifier: CC-BY-2.5 + - alias: + - Creative Commons Attribution Non Commercial 1.0 Generic + - Creative Commons Attribution-NonCommercial 1.0 + identifier: CC-BY-NC-1.0 + - alias: + - Creative Commons Attribution Non Commercial 2.0 Generic + - Creative Commons Attribution-NonCommercial 2.0 + identifier: CC-BY-NC-2.0 + - alias: + - Creative Commons Attribution Non Commercial 2.5 Generic + - Creative Commons Attribution-NonCommercial 2.5 + identifier: CC-BY-NC-2.5 + - alias: + - Creative Commons Attribution Non Commercial 3.0 Unported + - Creative Commons Attribution-NonCommercial 3.0 + identifier: CC-BY-NC-3.0 + - alias: + - Creative Commons Attribution Non Commercial 4.0 International + - Creative Commons Attribution-NonCommercial 4.0 + identifier: CC-BY-NC-4.0 + - alias: + - Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic + - Creative Commons Attribution-NoDerivs-NonCommercial 1.0 + identifier: CC-BY-NC-ND-1.0 + - alias: + - Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic + - Creative Commons Attribution-NonCommercial-NoDerivs 2.0 + identifier: CC-BY-NC-ND-2.0 + - alias: + - Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic + - Creative Commons Attribution-NonCommercial-NoDerivs 2.5 + identifier: CC-BY-NC-ND-2.5 + - alias: + - Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported + - Creative Commons Attribution-NonCommercial-NoDerivs 3.0 + identifier: CC-BY-NC-ND-3.0 + - alias: + - Creative Commons Attribution Non Commercial No Derivatives 4.0 International + - Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 + identifier: CC-BY-NC-ND-4.0 + - alias: + - Creative Commons Attribution Non Commercial Share Alike 1.0 Generic + - Creative Commons Attribution-NonCommercial-ShareAlike 1.0 + identifier: CC-BY-NC-SA-1.0 + - alias: + - Creative Commons Attribution Non Commercial Share Alike 2.0 Generic + - Creative Commons Attribution-NonCommercial-ShareAlike 2.0 + identifier: CC-BY-NC-SA-2.0 + - alias: + - Creative Commons Attribution Non Commercial Share Alike 2.5 Generic + - Creative Commons Attribution-NonCommercial-ShareAlike 2.5 + identifier: CC-BY-NC-SA-2.5 + - alias: + - Creative Commons Attribution Non Commercial Share Alike 3.0 Unported + - Creative Commons Attribution-NonCommercial-ShareAlike 3.0 + identifier: CC-BY-NC-SA-3.0 + - alias: + - Creative Commons Attribution Non Commercial Share Alike 4.0 International + - Creative Commons Attribution-NonCommercial-ShareAlike 4.0 + identifier: CC-BY-NC-SA-4.0 + - alias: + - Creative Commons Attribution No Derivatives 1.0 Generic + - Creative Commons Attribution-NoDerivs 1.0 + identifier: CC-BY-ND-1.0 + - alias: + - Creative Commons Attribution No Derivatives 2.0 Generic + - Creative Commons Attribution-NoDerivs 2.0 + identifier: CC-BY-ND-2.0 + - alias: + - Creative Commons Attribution No Derivatives 2.5 Generic + - Creative Commons Attribution-NoDerivs 2.5 + identifier: CC-BY-ND-2.5 + - alias: + - Creative Commons Attribution No Derivatives 3.0 Unported + - Creative Commons Attribution-NoDerivs 3.0 + identifier: CC-BY-ND-3.0 + - alias: + - Creative Commons Attribution No Derivatives 4.0 International + - Creative Commons Attribution-NoDerivatives 4.0 + identifier: CC-BY-ND-4.0 + - alias: + - Creative Commons Attribution Share Alike 2.0 Generic + - Creative Commons Attribution-ShareAlike 2.0 + identifier: CC-BY-SA-2.0 + - alias: + - Creative Commons Attribution Share Alike 2.5 Generic + - Creative Commons Attribution-ShareAlike 2.5 + identifier: CC-BY-SA-2.5 + - alias: + - Creative Commons Public Domain Dedication and Certification + identifier: CC-PDDC + - alias: + - Community Data License Agreement Permissive 1.0 + - Community Data License Agreement - Permissive, Version 1.0 + identifier: CDLA-Permissive-1.0 + - alias: + - Community Data License Agreement Sharing 1.0 + - Community Data License Agreement - Sharing, Version 1.0 + identifier: CDLA-Sharing-1.0 + - alias: + - CeCILL Free Software License Agreement v1.0 + - CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL + identifier: CECILL-1.0 + - alias: + - CeCILL Free Software License Agreement v1.1 + - FREE SOFTWARE LICENSING AGREEMENT CeCILL + identifier: CECILL-1.1 + - alias: + - CERN Open Hardware Licence v1.1 + - CERN OHL v1.1 + identifier: CERN-OHL-1.1 + - alias: + - CERN Open Hardware Licence v1.2 + - CERN OHL v1.2 + identifier: CERN-OHL-1.2 + - alias: + - CERN Open Hardware Licence Version 2 - Permissive + identifier: CERN-OHL-P-2.0 + - alias: + - CERN Open Hardware Licence Version 2 - Strongly Reciprocal + identifier: CERN-OHL-S-2.0 + - alias: + - CERN Open Hardware Licence Version 2 - Weakly Reciprocal + identifier: CERN-OHL-W-2.0 + - alias: + - CNRI Jython License + - JPython or CNRI + identifier: CNRI-Jython + - alias: + - CNRI Python Open Source GPL Compatible License Agreement + - CNRI OPEN SOURCE GPL-COMPATIBLE LICENSE AGREEMENT + identifier: CNRI-Python-GPL-Compatible + - alias: + - copyleft-next 0.3.0 + identifier: copyleft-next-0.3.0 + - alias: + - copyleft-next 0.3.1 + identifier: copyleft-next-0.3.1 + - alias: + - Code Project Open License 1.02 + - Code Project Open License (CPOL) 1.02 + identifier: CPOL-1.02 + - alias: + - Crossword License + - cwpuzzle.dtx + identifier: Crossword + - alias: + - CrystalStacker License + - Crystal Stacker + identifier: CrystalStacker + - alias: + - Cube License + - Cube game engine source code + identifier: Cube + - alias: + - curl License + - Copyright (c) 1996 - 2021, Daniel Stenberg + identifier: curl + - alias: + - Deutsche Freie Software Lizenz + identifier: D-FSL-1.0 + - alias: + - diffmark license + - 1. you can do what you want with it 2. I refuse any responsibility for the consequences + identifier: diffmark + - alias: + - DOC License + - DOC software + identifier: DOC + - alias: + - Dotseqn License + - Copyright (C) 1995 by Donald Arseneau + identifier: Dotseqn + - alias: + - DSDP License + - owned by The United States Government, and operated by the University of Chicago + identifier: DSDP + - alias: + - dvipdfm License + - should be distributed with a *different* name + identifier: dvipdfm + - alias: + - eGenix.com Public License 1.1.0 + - EGENIX.COM PUBLIC LICENSE AGREEMENT Version 1.1.0 + identifier: eGenix + - alias: + - Erlang Public License v1.1 + - ERLANG PUBLIC LICENSE Version 1.1 + identifier: ErlPL-1.1 + - alias: + - Etalab Open License 2.0 + - OPEN LICENCE 2.0 LICENCE OUVERTE 2.0 + identifier: etalab-2.0 + - alias: + - European Union Public License 1.0 + - European Union Public Licence V.1.0 + identifier: EUPL-1.0 + - alias: + - Eurosym License + - Copyright (c) 1999-2002 Henrik Theiling Licence Version 2 + identifier: Eurosym + - alias: + - FreeImage Public License v1.0 + - FreeImage Public License - Version 1.0 + identifier: FreeImage + - alias: + - FSF Unlimited License + - Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. + identifier: FSFUL + - alias: + - FSF Unlimited License (with License Retention) + - Copyright 1996-2006 Free Software Foundation, Inc. + identifier: FSFULLR + - alias: + - Giftware License + - Allegro 4 (the giftware license) + identifier: Giftware + - alias: + - 3dfx Glide License + - 3dfx GLIDE Source Code General Public License + identifier: Glide + - alias: + - Glulxe License + identifier: Glulxe + - alias: + - Haskell Language Report License + identifier: HaskellReport + - alias: + - Hippocratic License 2.1 + identifier: Hippocratic-2.1 + - alias: + - Historical Permission Notice and Disclaimer - sell variant + identifier: HPND-sell-variant + - alias: + - IBM PowerPC Initialization and Boot Software + identifier: IBM-pibs + - alias: + - Info-ZIP License + identifier: Info-ZIP + - alias: + - Intel ACPI Software License Agreement + identifier: Intel-ACPI + - alias: + - Japan Network Information Center License + identifier: JPNIC + - alias: + - JSON License + identifier: JSON + - alias: + - Licence Art Libre 1.2 + identifier: LAL-1.2 + - alias: + - Licence Art Libre 1.3 + identifier: LAL-1.3 + - alias: + - Latex2e License + identifier: Latex2e + - alias: + - Lesser General Public License For Linguistic Resources + identifier: LGPLLR + - alias: + - libselinux public domain notice + identifier: libselinux-1.0 + - alias: + - Linux Kernel Variant of OpenIB.org license + identifier: Linux-OpenIB + - alias: + - LaTeX Project Public License v1.0 + identifier: LPPL-1.0 + - alias: + - LaTeX Project Public License v1.1 + identifier: LPPL-1.1 + - alias: + - MakeIndex License + identifier: MakeIndex + - alias: + - MIT No Attribution + identifier: MIT-0 + - alias: + - Enlightenment License (e16) + identifier: MIT-advertising + - alias: + - CMU License + identifier: MIT-CMU + - alias: + - enna License + identifier: MIT-enna + - alias: + - feh License + identifier: MIT-feh + - alias: + - mpich2 License + identifier: mpich2 + - alias: + - Matrix Template Library License + identifier: MTLL + - alias: + - Mup License + identifier: Mup + - alias: + - Net Boolean Public License v1 + identifier: NBPL-1.0 + - alias: + - Non-Commercial Government Licence + identifier: NCGL-UK-2.0 + - alias: + - Net-SNMP License + identifier: Net-SNMP + - alias: + - Newsletr License + identifier: Newsletr + - alias: + - Norwegian Licence for Open Government Data + identifier: NLOD-1.0 + - alias: + - No Limit Public License + identifier: NLPL + - alias: + - Noweb License + identifier: Noweb + - alias: + - NRL License + identifier: NRL + - alias: + - NTP No Attribution + identifier: NTP-0 + - alias: + - Open Use of Data Agreement v1.0 + identifier: O-UDA-1.0 + - alias: + - Open CASCADE Technology Public License + identifier: OCCT-PL + - alias: + - Open Data Commons Attribution License v1.0 + identifier: ODC-By-1.0 + - alias: + - SIL Open Font License 1.0 with no Reserved Font Name + identifier: OFL-1.0-no-RFN + - alias: + - SIL Open Font License 1.0 with Reserved Font Name + identifier: OFL-1.0-RFN + - alias: + - OGC Software License, Version 1.0 + identifier: OGC-1.0 + - alias: + - Open Government Licence - Canada + identifier: OGL-Canada-2.0 + - alias: + - Open Government Licence v1.0 + identifier: OGL-UK-1.0 + - alias: + - Open Government Licence v2.0 + identifier: OGL-UK-2.0 + - alias: + - Open Government Licence v3.0 + identifier: OGL-UK-3.0 + - alias: + - Open Public License v1.0 + identifier: OPL-1.0 + - alias: + - The Parity Public License 6.0.0 + identifier: Parity-6.0.0 + - alias: + - The Parity Public License 7.0.0 + identifier: Parity-7.0.0 + - alias: + - ODC Public Domain Dedication & License 1.0 + identifier: PDDL-1.0 + - alias: + - PolyForm Noncommercial License 1.0.0 + identifier: PolyForm-Noncommercial-1.0.0 + - alias: + - PolyForm Small Business License 1.0.0 + identifier: PolyForm-Small-Business-1.0.0 + - alias: + - Python Software Foundation License 2.0 + identifier: PSF-2.0 + - alias: + - psfrag License + identifier: psfrag + - alias: + - Rdisc License + identifier: Rdisc + - alias: + - Red Hat eCos Public License v1.1 + identifier: RHeCos-1.1 + - alias: + - RSA Message-Digest License + identifier: RSA-MD + - alias: + - Sax Public Domain Notice + identifier: SAX-PD + - alias: + - SCEA Shared Source License + identifier: SCEA + - alias: + - SGI Free Software License B v1.0 + identifier: SGI-B-1.0 + - alias: + - SGI Free Software License B v1.1 + identifier: SGI-B-1.1 + - alias: + - Solderpad Hardware License v0.5 + identifier: SHL-0.5 + - alias: + - Solderpad Hardware License, Version 0.51 + identifier: SHL-0.51 + - alias: + - Sun Industry Standards Source License v1.2 + - SUN INDUSTRY STANDARDS SOURCE LICENSE Version 1.2 + identifier: SISSL-1.2 + - alias: + - Secure Messaging Protocol Public License + identifier: SMPPL + - alias: + - Spencer License 86 + identifier: Spencer-86 + - alias: + - Spencer License 94 + identifier: Spencer-94 + - alias: + - Spencer License 99 + identifier: Spencer-99 + - alias: + - SSH OpenSSH license + identifier: SSH-OpenSSH + - alias: + - SSH short notice + identifier: SSH-short + - alias: + - Server Side Public License, v 1 + identifier: SSPL-1.0 + - alias: + - SugarCRM Public License v1.1.3 + identifier: SugarCRM-1.1.3 + - alias: + - TAPR Open Hardware License v1.0 + identifier: TAPR-OHL-1.0 + - alias: + - TCP Wrappers License + identifier: TCP-wrappers + - alias: + - TMate Open Source License + identifier: TMate + - alias: + - TORQUE v2.5+ Software License v1.1 + identifier: TORQUE-1.1 + - alias: + - Trusster Open Source License + identifier: TOSL + - alias: + - Technische Universitaet Berlin License 1.0 + identifier: TU-Berlin-1.0 + - alias: + - Technische Universitaet Berlin License 2.0 + identifier: TU-Berlin-2.0 + - alias: + - VOSTROM Public License for Open Source + identifier: VOSTROM + - alias: + - W3C Software Notice and License (1998-07-20) + identifier: W3C-19980720 + - alias: + - W3C Software Notice and Document License (2015-05-13) + identifier: W3C-20150513 + - alias: + - Wsuipa License + identifier: Wsuipa + - alias: + - Xerox License + identifier: Xerox + - alias: + - XSkat License + identifier: XSkat + - alias: + - Yahoo! Public License v1.0 + identifier: YPL-1.0 + - alias: + - Zed License + identifier: Zed + - alias: + - Zimbra Public License v1.4 + identifier: Zimbra-1.4 + - alias: + - Zope Public License 1.1 + identifier: ZPL-1.1 + - alias: + - Commons Clause License v1.0 + identifier: Commons Clause + - alias: + - DMIT License + identifier: DMIT + - alias: + - GNU Affero General Public License v3.0 only + - AGPLv3 + - AGPL-3.0 + identifier: AGPL-3.0-only + - alias: + - GNU Affero General Public License v3.0 or later + - AGPLv3+ + identifier: AGPL-3.0-or-later + - alias: + - Adaptive Public License 1.0 + identifier: APL-1.0 + - alias: + - Apple Public Source License 1.0 + - Apple Public Source License version 1.0 + identifier: APSL-1.0 + - alias: + - Apple Public Source License 1.1 + - Apple Public Source License version 1.1 + identifier: APSL-1.1 + - alias: + - Apple Public Source License 1.2 + - Apple Public Source License version 1.2 + identifier: APSL-1.2 + - alias: + - Artistic License 1.0 + - Artistic 1.0 + identifier: Artistic-1.0 + - alias: + - Artistic License 1.0 w/clause 8 + identifier: Artistic-1.0-cl8 + - alias: + - Artistic License 1.0 (Perl) + - (perl) + - Perl + - "license: perl" + identifier: Artistic-1.0-Perl + - alias: + - Lawrence Berkeley National Labs BSD variant license + identifier: BSD-3-Clause-LBNL + - alias: + - Cryptographic Autonomy License 1.0 + identifier: CAL-1.0 + - alias: + - Cryptographic Autonomy License 1.0 (Combined Work Exception) + identifier: CAL-1.0-Combined-Work-Exception + - alias: + - CeCILL Free Software License Agreement v2.1 + identifier: CECILL-2.1 + - alias: + - CUA Office Public License v1.0 + identifier: CUA-OPL-1.0 + - alias: + - Educational Community License v1.0 + identifier: ECL-1.0 + - alias: + - Eiffel Forum License v1.0 + identifier: EFL-1.0 + - alias: + - Entessa Public License v1.0 + identifier: Entessa + - alias: + - Frameworx Open License 1.0 + identifier: Frameworx-1.0 + - alias: + - "Licence Libre du Quebec - Permissive version 1.1" + identifier: LiLiQ-P-1.1 + - alias: + - "Licence Libre du Quebec - Reciprocite version 1.1" + identifier: LiLiQ-R-1.1 + - alias: + - "Licence Libre du Quebec - Reciprocite forte version 1.1" + identifier: LiLiQ-Rplus-1.1 + - alias: + - Lucent Public License Version 1.0 + identifier: LPL-1.0 + - alias: + - Mozilla Public License 2.0 (no copyleft exception) + identifier: MPL-2.0-no-copyleft-exception + - alias: + - Multics License + identifier: Multics + - alias: + - NASA Open Source Agreement 1.3 + identifier: NASA-1.3 + - alias: + - Non-Profit Open Software License 3.0 + identifier: NPOSL-3.0 + - alias: + - OCLC Research Public License 2.0 + identifier: OCLC-2.0 + - alias: + - SIL Open Font License 1.1 with no Reserved Font Name + identifier: OFL-1.1-no-RFN + - alias: + - SIL Open Font License 1.1 with Reserved Font Name + identifier: OFL-1.1-RFN + - alias: + - Open Group Test Suite License + identifier: OGTSL + - alias: + - OSET Public License version 2.1 + identifier: OSET-PL-2.1 + - alias: + - Reciprocal Public License 1.1 + identifier: RPL-1.1 + - alias: + - Reciprocal Public License 1.5 + identifier: RPL-1.5 + - alias: + - Ricoh Source Code Public License + identifier: RSCPL + - alias: + - Simple Public License 2.0 + identifier: SimPL-2.0 + - alias: + - Upstream Compatibility License v1.0 + identifier: UCL-1.0 + - alias: + - Sybase Open Watcom Public License 1.0 + identifier: Watcom-1.0 + - alias: + - X.Net License + identifier: Xnet + - alias: + - Artistic license + identifier: artistic + - alias: + - Artistic license 2.0 + identifier: artistic-2.0 + - alias: + - Verbatim License + - verbatim copying + identifier: Verbatim + - alias: + - Liberation Font License + - LIBERATION font software + identifier: Liberation + - alias: + - Trusted Computing Group License + - Trusted Computing Group (TCG) + identifier: TCGL + - alias: + - Nmap License + - NMAP LICENSE + identifier: Nmap + - alias: + - gnuplot License + identifier: gnuplot + - alias: + - Distributed Management Task Force + identifier: DMTF + Good Licenses: + - alias: + - BSD Zero Clause License + - Zero-Clause BSD + - Copyright (C) 2006 by Rob Landley + identifier: 0BSD + - alias: + - Attribution Assurance License + - ATTRIBUTION ASSURANCE LICENSE + identifier: AAL + - alias: + - Academic Free License v1.1 + - Academic Free License version 1.1 + identifier: AFL-1.1 + - alias: + - Academic Free License v1.2 + - Academic Free License version 1.2 + identifier: AFL-1.2 + - alias: + - Academic Free License v2.0 + - Academic Free License version 2.0 + identifier: AFL-2.0 + - alias: + - Academic Free License v2.1 + - Academic Free License version 2.1 + - AFLv2.1 + identifier: AFL-2.1 + - alias: + - Academic Free License v3.0 + - Academic Free License version 3.0 + - AFL + identifier: AFL-3.0 + - alias: + - Apache License 1.1 + - Apache Software License, Version 1.1 + - ASL-1.1 + - ASL 1.1 + identifier: Apache-1.1 + - alias: + - Apache License 2.0 + - Apache License v2.0 + - Apache 2.0 License + - Apache License Version 2.0 + - Apache Software License 2.0 + - "http://www.apache.org/licenses/LICENSE-2.0" + - Apache-2 + - Apache2 + - Apache 2 + - Apache2.0 + - Apache 2.0 + - Apachev2 + - Apache v2 + - Apache v2.0 + - ASL-2.0 + - ASL 2.0 + - ASL2.0 + identifier: Apache-2.0 + - alias: + - Apple Public Source License 2.0 + - Apple Public Source License Version 2.0 + - APSL 2.0 + identifier: APSL-2.0 + - alias: + - Artistic License 2.0 + - Copyright (c) 2000-2006, The Perl Foundation + - Artistic 2.0 + identifier: Artistic-2.0 + - alias: + - BSD 1-Clause License + identifier: BSD-1-Clause + - alias: + - BSD 2-Clause "Simplified" License + - BSD 2-Clause + - BSD-2-Clause + - 2-clause BSD License + - BSD 3-Clause "New" or "Revised" License + - BSD-3-Clause + identifier: BSD + - alias: + - BSD-2-Clause Plus Patent License + identifier: BSD-2-Clause-Patent + - alias: + - Boost Software License 1.0 + - Boost Software License - Version 1.0 + - Boost + identifier: BSL-1.0 + - alias: + - Computer Associates Trusted Open Source License 1.1 + - COMPUTER ASSOCIATES TRUSTED OPEN SOURCE LICENSE + - Computer Associates Trusted Open Source License Version 1.1 + - CATOSL + identifier: CATOSL-1.1 + - alias: + - Common Development and Distribution License 1.0 + - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + identifier: CDDL-1.0 + - alias: + - CNRI Python License + - CNRIs License Agreement is retained in Python 1.6b1 + - CNRI OPEN SOURCE LICENSE AGREEMENT + identifier: CNRI-Python + - alias: + - Common Public Attribution License 1.0 + - Common Public Attribution License Version 1.0 (CPAL) + - CPAL + identifier: CPAL-1.0 + - alias: + - Common Public License 1.0 + - Common Public License Version 1.0 + - CPL + identifier: CPL-1.0 + - alias: + - Educational Community License v2.0 + - Educational Community License version 2.0 + - ECL 2.0 + identifier: ECL-2.0 + - alias: + - Eiffel Forum License v2.0 + - Eiffel Forum License, version 2 + - EFL 2.0 + identifier: EFL-2.0 + - alias: + - Eclipse Public License 1.0 + - Eclipse Public License v1.0 + - Eclipse Public License - v 1.0 + identifier: EPL-1.0 + - alias: + - Eclipse Public License 2.0 + - Eclipse Public License v2.0 + - Eclipse Public License - v 2.0 + identifier: EPL-2.0 + - alias: + - EU DataGrid Software License + - EU Datagrid + identifier: EUDatagrid + - alias: + - European Union Public License 1.1 + - EUPL V.1.1 + - EUPL 1.1 + identifier: EUPL-1.1 + - alias: + - European Union Public License 1.2 + - EUPL v. 1.2 + - EUPL 1.2 + identifier: EUPL-1.2 + - alias: + - Fair License + identifier: Fair + - alias: + - GNU General Public License v2.0 only + - GNU GENERAL PUBLIC LICENSE Version 2 + - GPL(==-2) + - GPL-2.0 + - GPL-2.0 License + - GPL2 + - GPLV2 + - GPLv2 + - GPL-2 + - GPL 2.0 + - GPL v2 + - GPL v2.0 + - GNU GPL v2 + identifier: GPL-2.0-only + - alias: + - GNU General Public License v2.0 or later + - GPL(>=-2) + - GPL(>=-2.0) + - GPL(>=2) + - GPL-2+ + - GPL-2.0+ + - GPLv2+ + - GPLV2+ + identifier: GPL-2.0-or-later + - alias: + - GNU General Public License v3.0 only + - version 3 of the GNU General Public License + - GPL-3.0 + - GPLV3 + - GPLv3 + - GPL3 + - GPL-3 + - GPL3.0 + identifier: GPL-3.0-only + - alias: + - GNU General Public License v3.0 or later + - GPL(>=-3) + - GPL(>=3) + - GPL-3+ + - GPL-3.0+ + - GPLv3+ + identifier: GPL-3.0-or-later + - alias: + - Historical Permission Notice and Disclaimer + identifier: HPND + - alias: + - Intel Open Source License + identifier: Intel + - alias: + - IPA Font License + identifier: IPA + - alias: + - ICU License + identifier: ICU + - alias: + - IBM Public License v1.0 + - IBM PUBLIC LICENSE + identifier: IPL-1.0 + - alias: + - ISC License + - ISC license + - Copyright © 2004-2013 by Internet Systems Consortium, Inc. (“ISC”) + identifier: ISC + - alias: + - GNU Library General Public License v2 only + - first released version of the library GPL + - GNU LIBRARY GENERAL PUBLIC LICENSE Version 2 + - LGPL-2.0 + - LGPLv2 + - LGPL-2 + - LGPL v2.0 + identifier: LGPL-2.0-only + - alias: + - GNU Library General Public License v2 or later + - LGPL(>=-2) + - LGPL(>=2) + - LGPL-2.0+ + - LGPL-2+ + - LGPLv2+ + identifier: LGPL-2.0-or-later + - alias: + - GNU Lesser General Public License v2.1 only + - GNU Library Public License, version 2, hence the version number 2.1 + - LGPL-2.1 + - LGPLv2.1 + - LGPL 2.1 + identifier: LGPL-2.1-only + - alias: + - GNU Lesser General Public License v2.1 or later + - LGPL(>=-2.1) + - LGPL-2.1+ + - LGPLv2.1+ + - LGPL v2.1 + identifier: LGPL-2.1-or-later + - alias: + - GNU Lesser General Public License v3.0 only + - version 3 of the GNU Lesser General Public License + - LGPL-3.0 + - LGPLv3 + - LGPL-3 + - LGPL v3 + identifier: LGPL-3.0-only + - alias: + - GNU Lesser General Public License v3.0 or later + - LGPL-3+ + - LGPL-3.0+ + - LGPLv3+ + - LGPL3+ + identifier: LGPL-3.0-or-later + - alias: + - Lucent Public License v1.02 + - Lucent Public License Version 1.02 + - LPL + identifier: LPL-1.02 + - alias: + - LaTeX Project Public License v1.3c + - LPPL Version 1.3c + identifier: LPPL-1.3c + - alias: + - The MirOS Licence + identifier: MirOS + - alias: + - MIT License + - MIT license + - MIT-LICENSE + - (MIT) + - (mit) + - "http://opensource.org/licenses/MIT" + - MIT/X + - expat + identifier: MIT + - alias: + - Motosoto License + - Motosoto Open Source License + identifier: Motosoto + - alias: + - Mozilla Public License 1.0 + - MOZILLA PUBLIC LICENSE Version 1.0 + - Mozilla Public License Version 1.0 + - MPLv1.0 + identifier: MPL-1.0 + - alias: + - Mozilla Public License 1.1 + - Mozilla Public License Version 1.1 + - MPLv1.1 + identifier: MPL-1.1 + - alias: + - Mozilla Public License 2.0 + - "https://mozilla.org/MPL/2.0/" + - Mozilla Public License, v. 2.0 + - Mozilla Public License Version 2.0 + - MPLv2.0 + - MPLv2 + - MPL-2 + - MPL2 + - MPL 2.0 + identifier: MPL-2.0 + - alias: + - Microsoft Public License + identifier: MS-PL + - alias: + - Microsoft Reciprocal License + identifier: MS-RL + - alias: + - Mulan Permissive Software License, Version 2 + - "http://license.coscl.org.cn/MulanPSL2" + - Mulan PSL v2 + - Mulan PSL V2 + - Mulan 2.0 + - Mulan v2 + identifier: MulanPSL-2.0 + - alias: + - Naumen Public License + identifier: Naumen + - alias: + - University of Illinois/NCSA Open Source License + identifier: NCSA + - alias: + - Nethack General Public License + identifier: NGPL + - alias: + - Nokia Open Source License + identifier: Nokia + - alias: + - NTP License + identifier: NTP + - alias: + - SIL Open Font License 1.1 + - SIL Open Font License, Version 1.1 + identifier: OFL-1.1 + - alias: + - Open Software License 1.0 + - Open Software License version 1.0 + - OSL 1.0 + identifier: OSL-1.0 + - alias: + - Open Software License 2.0 + - Open Software License version 2.0 + - OSL 2.0 + identifier: OSL-2.0 + - alias: + - Open Software License 2.1 + - Open Software License version 2.1 + - OSL 2.1 + identifier: OSL-2.1 + - alias: + - Open Software License 3.0 + - Open Software License version 3.0 + - OSL 3.0 + identifier: OSL-3.0 + - alias: + - PHP License v3.0 + - The PHP License version 3.0 + identifier: PHP-3.0 + - alias: + - PostgreSQL License + identifier: PostgreSQL + - alias: + - Python License 2.0 + - Python License, Version 2 + - PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 + identifier: Python-2.0 + - alias: + - Q Public License 1.0 + - Q Public License version 1.0 + identifier: QPL-1.0 + - alias: + - RealNetworks Public Source License v1.0 + - RealNetworks Public Source License version 1.0 + identifier: RPSL-1.0 + - alias: + - Sun Industry Standards Source License v1.1 + identifier: SISSL + - alias: + - Sleepycat License + - Sleepycat Software + identifier: Sleepycat + - alias: + - Sun Public License v1.0 + - SUN PUBLIC LICENSE Version 1.0 + - Sun Public License Version 1.0 + identifier: SPL-1.0 + - alias: + - Universal Permissive License v1.0 + - Universal Permissive License (UPL), Version 1.0 + identifier: UPL-1.0 + - alias: + - Vovida Software License v1.0 + - The Vovida Software License, Version 1.0 + identifier: VSL-1.0 + - alias: + - W3C Software Notice and License (2002-12-31) + - "http://www.w3.org/Consortium/Legal/2002" + identifier: W3C + - alias: + - zlib License + - zlib + - zlib/libpng + identifier: Zlib + - alias: + - Zope Public License 2.0 + - Zope Public License (ZPL) Version 2.0 + - ZPLv2.0 + - ZPL 2.0 + identifier: ZPL-2.0 + - alias: + - Mulan Permissive Software License v1 + - Mulan PSL v1 + - Mulan v1.0 + - Mulan 2.0 + identifier: MulanPSL-1.0 + - alias: + - Zope Public License 2.1 + - Zope Public License (ZPL) Version 2.1 + - ZPLv2.1 + - ZPL 2.1 + identifier: ZPL-2.1 + - alias: + - OpenSSL License + - "http://www.openssl.org/" + identifier: OpenSSL + - alias: + - Independent JPEG Group License + - Independent JPEG Group + identifier: IJG + - alias: + - Creative Commons Attribution 1.0 Generic + - Creative Commons Attribution 1.0 + identifier: CC-BY-1.0 + - alias: + - Creative Commons Attribution 3.0 Unported + - Creative Commons Attribution 3.0 + identifier: CC-BY-3.0 + - alias: + - Creative Commons Attribution Share Alike 1.0 Generic + - Creative Commons Attribution-ShareAlike 1.0 + identifier: CC-BY-SA-1.0 + - alias: + - Creative Commons Attribution 4.0 International + identifier: CC-BY-4.0 + - alias: + - Creative Commons Attribution Share Alike 4.0 International + - Creative Commons Attribution-ShareAlike 4.0 International + identifier: CC-BY-SA-4.0 + - alias: + - Ruby License + identifier: Ruby + - alias: + - Creative Commons Zero v1.0 Universal + - CC0 + identifier: CC0-1.0 + - alias: + - TCL/TK License + - copyrighted by the Regents of the University of California, Sun Microsystems, Inc. + identifier: TCL + - alias: + - zlib/libpng License with Acknowledgement + - Copyright (c) 2002-2007 Charlie Poole + - zlib with acknowledgement + - zlib/libpng License with Acknowledgement + identifier: zlib-acknowledgement + - alias: + - The Unicode Character Database + identifier: UCD + - alias: + - GNU Free Documentation License v1.3 only + - GNU Free Documentation License, Version 1.3 + - GFDLv1.3 + - GFDL 1.3 + - GFDL-1.3 + identifier: GFDL-1.3-only + - alias: + - GNU General Public License v1.0 only + - GNU General Public License, version 1 + - GPL v1.0 + - GPL 1.0 + - GPL-1.0 + identifier: GPL-1.0-only + - alias: + - GNU General Public License v1.0 or later + - GPL+ + - GPL v1.0+ + - GPL 1.0+ + - GPL-1.0+ + - GPLv1+ + identifier: GPL-1.0-or-later + - alias: + - Common Development and Distribution License 1.1 + - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1 + identifier: CDDL-1.1 + - alias: + - Creative Commons Attribution Share Alike 3.0 Unported + - Attribution-ShareAlike 3.0 + identifier: CC-BY-SA-3.0 + - alias: + - NetCDF license + - Copyright 1993-2014 University Corporation for Atmospheric Research/Unidata + identifier: NetCDF + - alias: + - ImageMagick License + - ImageMagick license + identifier: ImageMagick + - alias: + - Do What The F*ck You Want To Public License + identifier: WTFPL + - alias: + - Freetype Project License + identifier: FTL + - alias: + - Saxpath License + identifier: Saxpath + - alias: + - JasPer License + identifier: JasPer-2.0 + - alias: + - XPP License + identifier: xpp + - alias: + - Unicode License Agreement - Data Files and Software (2015) + identifier: Unicode-DFS-2015 + - alias: + - Unicode License Agreement - Data Files and Software (2016) + identifier: Unicode-DFS-2016 + - alias: + - Unicode Terms of Use + identifier: Unicode-TOU + - alias: + - Sendmail License + identifier: Sendmail + - alias: + - Sendmail License 8.23 + identifier: Sendmail-8.23 + - alias: + - Open LDAP Public License v1.1 + identifier: OLDAP-1.1 + - alias: + - Open LDAP Public License v1.2 + identifier: OLDAP-1.2 + - alias: + - Open LDAP Public License v1.3 + identifier: OLDAP-1.3 + - alias: + - Open LDAP Public License v1.4 + identifier: OLDAP-1.4 + - alias: + - Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B) + identifier: OLDAP-2.0 + - alias: + - Open LDAP Public License v2.0.1 + identifier: OLDAP-2.0.1 + - alias: + - Open LDAP Public License v2.1 + identifier: OLDAP-2.1 + - alias: + - Open LDAP Public License v2.2 + identifier: OLDAP-2.2 + - alias: + - Open LDAP Public License v2.2.1 + identifier: OLDAP-2.2.1 + - alias: + - Open LDAP Public License 2.2.2 + identifier: OLDAP-2.2.2 + - alias: + - Open LDAP Public License v2.4 + identifier: OLDAP-2.4 + - alias: + - Open LDAP Public License v2.5 + identifier: OLDAP-2.5 + - alias: + - Open LDAP Public License v2.6 + identifier: OLDAP-2.6 + - alias: + - Open LDAP Public License v2.8 + identifier: OLDAP-2.8 + - alias: + - libtiff License + identifier: libtiff + - alias: + - libpng License + - libpng-1.0.6 + - LibPng + identifier: Libpng + - alias: + - PNG Reference Library version 2 + identifier: libpng-2.0 + - alias: + - Arphic Public License + - ARPHIC PUBLIC LICENSE + identifier: Arphic + - alias: + - Adobe Postscript AFM License + - 14 PostScript(R) AFM files + identifier: APAFML + - alias: + - xinetd License + - allowing xinetd as maintained by me (Rob Braun) + identifier: xinetd + - alias: + - Vim License + identifier: Vim + - alias: + - Scheme Widget Library (SWL) Software License Agreement + identifier: SWL + - alias: + - Original SSLeay License + identifier: SSLeay + - alias: + - SNIA Public License 1.1 + - Storage Networking Industry Association + identifier: SNIA + - alias: + - Qhull License + - www.qhull.org + identifier: Qhull + - alias: + - psutils License + - PS Utilities Package + identifier: psutils + - alias: + - Plexus Classworlds License + - "http://classwords.codehaus.org/" + identifier: Plexus + - alias: + - Open Market License + - copyrighted by Open Market + identifier: OML + - alias: + - Old FSF Documentation License + identifier: OFSFDL + - alias: + - MIT +no-false-attribs License + identifier: MITNFA + - alias: + - Leptonica License + - Leptonica + identifier: Leptonica + - alias: + - Interbase Public License v1.0 + - INTERBASE PUBLIC LICENSE Version 1.0 + identifier: Interbase-1.0 + - alias: + - GL2PS License + - GL2PS LICENSE Version 2 + identifier: GL2PS + - alias: + - FSF All Permissive License + identifier: FSFAP + - alias: + - BSD Protection License + identifier: BSD-Protection + - alias: + - BSD with attribution + - "Universidad de Palermo, Argentina" + identifier: BSD-3-Clause-Attribution + - alias: + - Afmparse License + - (C) 1988, 1989 by Adobe Systems Incorporated + identifier: Afmparse + - alias: + - public domain + identifier: Public Domain + - alias: + - Zend License v2.0 + identifier: Zend-2.0 + Need Review Licenses: + - alias: + - Apache License 1.0 + - Copyright (c) 1995-1999 The Apache Group + - ASL 1.0 + identifier: Apache-1.0 + - alias: + - BitTorrent Open Source License v1.1 + identifier: BitTorrent-1.1 + - alias: + - BSD 2-Clause FreeBSD License + identifier: BSD-2-Clause-FreeBSD + - alias: + - BSD 3-Clause Clear License + identifier: BSD-3-Clause-Clear + - alias: + - BSD 4-Clause "Original" or "Old" License + identifier: BSD-4-Clause + - alias: + - CeCILL Free Software License Agreement v2.0 + identifier: CECILL-2.0 + - alias: + - CeCILL-B Free Software License Agreement + identifier: CECILL-B + - alias: + - CeCILL-C Free Software License Agreement + identifier: CECILL-C + - alias: + - Clarified Artistic License + identifier: ClArtistic + - alias: + - Condor Public License v1.1 + identifier: Condor-1.1 + - alias: + - GNU Free Documentation License v1.1 only + identifier: GFDL-1.1-only + - alias: + - GNU Free Documentation License v1.1 or later + identifier: GFDL-1.1-or-later + - alias: + - GNU Free Documentation License v1.2 only + identifier: GFDL-1.2-only + - alias: + - GNU Free Documentation License v1.2 or later + identifier: GFDL-1.2-or-later + - alias: + - GNU Free Documentation License v1.3 or later + identifier: GFDL-1.3-or-later + - alias: + - gSOAP Public License v1.3b + identifier: gSOAP-1.3b + - alias: + - iMatix Standard Function Library Agreement + identifier: iMatix + - alias: + - Imlib2 License + identifier: Imlib2 + - alias: + - LaTeX Project Public License v1.2 + identifier: LPPL-1.2 + - alias: + - LaTeX Project Public License v1.3a + identifier: LPPL-1.3a + - alias: + - Netizen Open Source License + identifier: NOSL + - alias: + - Netscape Public License v1.0 + identifier: NPL-1.0 + - alias: + - Netscape Public License v1.1 + identifier: NPL-1.1 + - alias: + - ODC Open Database License v1.0 + identifier: ODbL-1.0 + - alias: + - SIL Open Font License 1.0 + identifier: OFL-1.0 + - alias: + - Open LDAP Public License v2.3 + identifier: OLDAP-2.3 + - alias: + - Open LDAP Public License v2.7 + identifier: OLDAP-2.7 + - alias: + - Open Software License 1.1 + - Open Software License version 1.1 + - OSL 1.1 + - OSL-1.1 + identifier: OSL-1.1 + - alias: + - PHP License v3.01 + identifier: PHP-3.01 + - alias: + - SGI Free Software License B v2.0 + identifier: SGI-B-2.0 + - alias: + - Standard ML of New Jersey License + identifier: SMLNJ + - alias: + - The Unlicense + identifier: Unlicense + - alias: + - X11 License + identifier: X11 + - alias: + - XFree86 License 1.1 + identifier: XFree86-1.1 + - alias: + - Yahoo! Public License v1.1 + identifier: YPL-1.1 + - alias: + - Zimbra Public License v1.3 + identifier: Zimbra-1.3 diff --git a/get_license/extract_util.py b/get_license/extract_util.py new file mode 100644 index 0000000000000000000000000000000000000000..c5932959e53afca83a4d0c8b15accba0fdb8ba13 --- /dev/null +++ b/get_license/extract_util.py @@ -0,0 +1,142 @@ +import os +import tarfile +import zipfile +import lzma +import filetype +import logging +import shutil + +log_check = logging.getLogger() + +def _extract_tar(tarball_path, extract_path): + """ + Extract tar package in extract_path. If extract failed the program will exit. + """ + res_flag = True + if not os.path.isfile(tarball_path): + log_check.error("%s is not a file", tarball_path) + res_flag = False + return res_flag + + try: + with tarfile.open(tarball_path) as content: + content.extractall(path=extract_path) + except FileNotFoundError: + res_flag = False + log_check.error("%s can not be found", tarball_path) + except tarfile.CompressionError: + res_flag = False + log_check.error("%s can not be decoded correctly", tarball_path) + except tarfile.ReadError: + res_flag = False + log_check.error("%s is invalid tar file", tarball_path) + finally: + return res_flag + + +def _extract_bz2(bz2_path, extract_path): + """ + Extract bz2 package in extract_path. If extract failed the program will exit. + """ + res_flag = True + if not os.path.isfile(bz2_path): + log_check.error("%s is not a bz2 file", bz2_path) + res_flag = False + return res_flag + + try: + archive = tarfile.open(bz2_path, 'r:bz2') + for tarinfo in archive: + archive.extract(tarinfo, extract_path) + archive.close() + except FileNotFoundError: + res_flag = False + log_check.error("%s can not be found", bz2_path) + except tarfile.CompressionError: + res_flag = False + log_check.error("%s can not be decoded correctly", bz2_path) + except tarfile.ReadError: + res_flag = False + log_check.error("%s is invalid tar file", bz2_path) + finally: + return res_flag + + +def _extract_zip(zip_path, extract_path): + """ + Extract zip package in extract_path. If extract failed the program will exit. + """ + res_flag = True + if not os.path.isfile(zip_path): + log_check.error("%s is not a zip file", zip_path) + res_flag = False + return res_flag + + try: + zip_file = zipfile.ZipFile(zip_path) + zip_file.extractall(extract_path) + zip_file.close() + except FileNotFoundError: + res_flag = False + log_check.error("%s can not be found", zip_path) + except zipfile.BadZipfile: + res_flag = False + log_check.error("%s is bad zip file", zip_path) + except zipfile.LargeZipFile: + res_flag = False + log_check.error("The zip file requires the zip64 feature but is not enabled") + finally: + return res_flag + + +def _extract_xz(xz_path, extract_path): + """ + extract tar.xz to specific path + """ + if not os.path.isfile(xz_path): + log_check.error("%s is not a file", xz_path) + return False + tar_file = os.path.join(extract_path, os.path.basename(xz_path).split(".xz")[0]) + try: + with lzma.open(xz_path, "rb") as file_input: + with open(tar_file, "wb") as output: + shutil.copyfileobj(file_input, output) + res = _extract_tar(tar_file, extract_path) + os.remove(tar_file) + return res + except FileNotFoundError: + log_check.error("%s can not found", xz_path) + return False + except lzma.LZMAError: + log_check.error("%s can not be decompressed", xz_path) + return False + + +def extract_tarball(tb_file, extract_path): + """ + Entrypoint for extract tarball + """ + method_map = { + "tar": _extract_tar, + "gz": _extract_tar, + "bz2": _extract_bz2, + "zip": _extract_zip, + "xz": _extract_xz + } + ft = filetype.guess(tb_file) + method = None if not ft else method_map.get(ft.extension, None) + if method: + return method(tb_file, extract_path) + log_check.error("filetype: %s not support to extract") + return False + + +def extract_all_pkg(tarballs, extract_path): + """ + Extract all tarballs + """ + extr_res = True + for tname in tarballs: + if not extract_tarball(tname, extract_path): + extr_res = False + return extr_res \ No newline at end of file diff --git a/get_license/get_license.py b/get_license/get_license.py new file mode 100644 index 0000000000000000000000000000000000000000..87fb972e26a47f73e7fff39a0a543d33b6b139d1 --- /dev/null +++ b/get_license/get_license.py @@ -0,0 +1,64 @@ +import argparse +import os + +from src.license_driver import parse_spec_license, parse_compressed_file_license, get_all_license,\ + load_config_new, LICENSE_YAML_PATH + +COMMAMD_NAME = { + "from-spec", + "from-src", + "from-all" +} + +def load_my_args(): + parser = argparse.ArgumentParser() + + subparsers = parser.add_subparsers(dest="cmd_name", + help="use sub command to choose get license from spec, compressed file or dir") + + spec_parser = subparsers.add_parser("from-spec", + help="get license from spec") + spec_parser.add_argument("-f", "--file", required=True, + help="file path of spec") + spec_parser.set_defaults(func = parse_spec_license) + + src_parser = subparsers.add_parser("from-src", + help="get license from compressed file") + src_parser.add_argument("-c", "--compresses", required=True, type=str, nargs="+", + help="file path of compressed") + src_parser.add_argument("-e", "--extract_dir", default="/tmp/get_licenses/pkg_src", + help="file dir of extract") + src_parser.set_defaults(func = parse_compressed_file_license) + + all_parser = subparsers.add_parser("from-all", + help="get license from compressed file") + all_parser.add_argument("-d", "--work_dir", required=True, + help="file path of parsed dir") + all_parser.add_argument("-e", "--extract_dir", default="/tmp/get_licenses/pkg_src", + help="file dir of extract") + all_parser.set_defaults(func = get_all_license) + + return parser.parse_args() + + +if __name__ == "__main__": + args = load_my_args() + + result = {} + spec_lic = {} + src_lic = {} + pl = load_config_new(LICENSE_YAML_PATH) + if not args.cmd_name or not args.cmd_name in COMMAMD_NAME: + print("not find cmd") + else: + if args.cmd_name == "from-all": + spec_lic, src_lic = args.func(args.work_dir, args.extract_dir, pl) + elif args.cmd_name == "from-src": + src_lic = args.func(args.compresses, args.extract_dir, pl) + elif args.cmd_name == "from-spec": + spec_lic = args.func(args.file, pl) + result["spec_license"] = spec_lic + result["src_license"] = src_lic + + print(result) + \ No newline at end of file diff --git a/get_license/license_driver.py b/get_license/license_driver.py new file mode 100644 index 0000000000000000000000000000000000000000..8d636fa01ac22493450a4295973ead90743f7a25 --- /dev/null +++ b/get_license/license_driver.py @@ -0,0 +1,110 @@ +from get_license.spec import Spec +from get_license.check_license import PkgLicense +from get_license.extract_util import extract_all_pkg + +import os +import logging +import subprocess +import shutil + +log_check = logging.getLogger() + +CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) +LICENSE_LIST_PATH = os.path.join(CURRENT_DIR, "config", "LicenseList.txt") +FULLNAME_LICENSE_PATH = os.path.join(CURRENT_DIR, "config", "license_translations") +LICENSE_YAML_PATH = os.path.join(CURRENT_DIR, "config", "Licenses.yaml") +UNUSED_FILE_SUFFIX = (".spec~", ".specfc", ".spec.old", ".osc") +COMPRESSED_TYPE = ("tar", "tar.xz", ".tar.gz", ".tgz", ".gz", ".zip", ".bz2") + +def get_parsed_spec(filename, parse_name="temp.spec"): + """ + split license str in spec file + + :return:{ + license's short name: friendlyness(white, black, unknow), + ... + } + """ + if not os.path.isfile(filename): + log_check.error("spec file %s not exist", os.path.basename(filename)) + return "" + parse_file = os.path.join(os.path.dirname(filename), parse_name) + cmd_list = ["rpmspec", "--parse", filename, ">", parse_file] + try: + sub_proc = subprocess.run(" ".join(cmd_list), + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + check=True, shell=True) + except subprocess.CalledProcessError as e: + log_check.error("parse spec failed: %s", e) + return "" + return parse_file + +def load_config(list_file, full_name_file): + if not os.path.isfile(list_file) or not os.path.isfile(full_name_file): + log_check.error("cannot find file") + return None + pl = PkgLicense() + PkgLicense.load_licenses_dict(pl.license_list, list_file) + PkgLicense.load_licenses_dict(pl.full_name_map, full_name_file) + return pl + + +def load_config_new(license_config): + if not os.path.isfile(license_config): + log_check.error("cannot find file") + return None + pl = PkgLicense() + pl.license_list, pl.full_name_map = PkgLicense.load_licenses_new(license_config) + return pl + + +def parse_spec_license(spec_file, pl): + parse_spec = get_parsed_spec(spec_file) + if not parse_spec: + parse_spec = spec_file + spec_licenses = pl.scan_licenses_in_spec(parse_spec, pl.full_name_map) + spec_lic_dict = {} + for lic in spec_licenses: + spec_lic_dict[lic] = pl.license_list.get(lic, "unknow") + os.remove(parse_spec) + return spec_lic_dict + + +def parse_compressed_file_license(files, extract_path, pl): + src_lic_dict = {} + _ = not os.path.exists(extract_path) and os.makedirs(extract_path) + extract_all_pkg(files, extract_path) + src_licenses = PkgLicense.scan_licenses_in_source(extract_path, pl.full_name_map) + for lic in src_licenses: + src_lic_dict[lic] = pl.license_list.get(lic, "unknow") + shutil.rmtree(extract_path) + return src_lic_dict + + +def get_all_license(work_dir, extract_path, pl): + compressed_files = [] + spec_file = None + license_in_spec = {} + license_in_src = {} + + for file_name in os.listdir(work_dir): + file_path = os.path.join(work_dir, file_name) + log_check.debug('Scanning file: %s', file_path) + if is_compressed_file(file_path): # save all file need to check license + compressed_files.append(file_path) + continue + if file_name.endswith(".spec"): + license_in_spec = parse_spec_license(file_path, pl) + + license_in_src = parse_compressed_file_license(compressed_files, extract_path, pl) + + return license_in_spec, license_in_src + +def is_compressed_file(src_file): + """ + Determine whether it is a compressed file. + """ + if os.path.isfile(src_file): + if src_file.endswith(COMPRESSED_TYPE): + return True + return False \ No newline at end of file diff --git a/get_license/spec.py b/get_license/spec.py new file mode 100644 index 0000000000000000000000000000000000000000..012c848c1111f79cab7b5f4bb6928ffe27a53f47 --- /dev/null +++ b/get_license/spec.py @@ -0,0 +1,422 @@ +"""Python module for parsing RPM spec files. + +RPMs are build from a package's sources along with a spec file. The spec file controls how the RPM +is built. This module allows you to parse spec files and gives you simple access to various bits of +information that is contained in the spec file. + +Current status: This module does not parse everything of a spec file. Only the pieces I needed. So +there is probably still plenty of stuff missing. However, it should not be terribly complicated to +add support for the missing pieces. + +""" + +import re +from abc import (ABCMeta, abstractmethod) + +__all__ = ['Spec', 'replace_macros', 'Package'] + + +class _Tag(metaclass=ABCMeta): + def __init__(self, name, pattern_obj, attr_type): + self.name = name + self.pattern_obj = pattern_obj + self.attr_type = attr_type + + def test(self, line): + return re.search(self.pattern_obj, line) + + def update(self, spec_obj, context, match_obj, line): + """Update given spec object and parse context and return them again. + + :param spec_obj: An instance of Spec class + :param context: The parse context + :param match_obj: The re.match object + :param line: The original line + :return: Given updated Spec instance and parse context dictionary. + """ + + assert spec_obj + assert context + assert match_obj + assert line + + return self.update_impl(spec_obj, context, match_obj, line) + + @abstractmethod + def update_impl(self, spec_obj, context, match_obj, line): + pass + + @staticmethod + def current_target(spec_obj, context): + target_obj = spec_obj + if context['current_subpackage'] is not None: + target_obj = context['current_subpackage'] + return target_obj + + +class _NameValue(_Tag): + """Parse a simple name → value tag.""" + + def __init__(self, name, pattern_obj, attr_type=None): + super().__init__(name, pattern_obj, attr_type if attr_type else str) + + def update_impl(self, spec_obj, context, match_obj, line): + target_obj = _Tag.current_target(spec_obj, context) + value = match_obj.group(1) + + # Sub-packages + if self.name == 'name': + spec_obj.packages = [] + spec_obj.packages.append(Package(value)) + + setattr(target_obj, self.name, self.attr_type(value)) + return spec_obj, context + + +class _MacroDef(_Tag): + """Parse global macro definitions.""" + + def __init__(self, name, pattern_obj): + super().__init__(name, pattern_obj, str) + + def update_impl(self, spec_obj, context, match_obj, line): + name, value = match_obj.groups() + setattr(spec_obj, name, str(value)) + return spec_obj, context + + +class _List(_Tag): + """Parse a tag that expands to a list.""" + + def __init__(self, name, pattern_obj): + super().__init__(name, pattern_obj, list) + + def update_impl(self, spec_obj, context, match_obj, line): + target_obj = _Tag.current_target(spec_obj, context) + + if not hasattr(target_obj, self.name): + setattr(target_obj, self.name, list()) + + value = match_obj.group(1) + if self.name == 'packages': + if value == '-n': + subpackage_name = line.rsplit(' ', 1)[-1].rstrip() + else: + subpackage_name = '{}-{}'.format(spec_obj.name, value) + package = Package(subpackage_name) + context['current_subpackage'] = package + package.is_subpackage = True + spec_obj.packages.append(package) + elif self.name in ['build_requires', 'requires', 'conflicts', 'obsoletes', 'provides']: + # Macros are valid in requirements + value = replace_macros(value, spec=spec_obj) + + # It's also legal to do: + # Requires: a b c + # Requires: b >= 3.1 + # Requires: a, b >= 3.1, c + + # 1. Tokenize + tokens = [val for val in re.split('[\t\n, ]', value) if val != ''] + values = [] + + # 2. Join + add = False + for val in tokens: + if add: + add = False + val = values.pop() + ' ' + val + elif val in ['>=', '!=', '>', '<', '<=', '==', '=']: + add = True # Add next value to this one + val = values.pop() + ' ' + val + values.append(val) + + for val in values: + requirement = Requirement(val) + getattr(target_obj, self.name).append(requirement) + else: + getattr(target_obj, self.name).append(value) + + return spec_obj, context + + +class _ListAndDict(_Tag): + """Parse a tag that expands to a list and to a dict.""" + + def __init__(self, name, pattern_obj): + super().__init__(name, pattern_obj, list) + + def update_impl(self, spec_obj, context, match_obj, line): + source_name, value = match_obj.groups() + dictionary = getattr(spec_obj, '{}_dict'.format(self.name)) + dictionary[source_name] = value + target_obj = _Tag.current_target(spec_obj, context) + try: + getattr(target_obj, self.name).append(value) + except AttributeError as ae: + pass + return spec_obj, context + + +_tags = [ + _NameValue('name', re.compile(r'^Name:\s*(\S+)')), + _NameValue('version', re.compile(r'^Version:\s*(\S+)')), + _NameValue('epoch', re.compile(r'^Epoch:\s*(\S+)'), attr_type=int), + _NameValue('release', re.compile(r'^Release:\s*(\S+)')), + _NameValue('summary', re.compile(r'^Summary:\s*(.+)')), + _NameValue('license', re.compile(r'^License:\s*(.+)')), + _NameValue('group', re.compile(r'^Group:\s*(\S+)')), + _NameValue('url', re.compile(r'^URL:\s*(\S+)', re.IGNORECASE)), + _NameValue('buildroot', re.compile(r'^BuildRoot:\s*(\S+)')), + _NameValue('buildarch', re.compile(r'^BuildArch:\s*(\S+)')), + _ListAndDict('sources', re.compile(r'^(Source\d*):\s*(\S+)')), + _ListAndDict('patches', re.compile(r'^(Patch\d*):\s*(\S+)')), + _List('build_requires', re.compile(r'^BuildRequires:\s*(.+)')), + _List('requires', re.compile(r'^Requires:\s*(.+)')), + _List('conflicts', re.compile(r'^Conflicts:\s*(.+)')), + _List('obsoletes', re.compile(r'^Obsoletes:\s*(.+)')), + _List('provides', re.compile(r'^Provides:\s*(.+)')), + _List('packages', re.compile(r'^%package\s+(\S+)')), + _MacroDef('define', re.compile(r'^%define\s+(\S+)\s+(\S+)')), + _MacroDef('global', re.compile(r'^%global\s+(\S+)\s+(\S+)')) +] + +_macro_pattern = re.compile(r'%{(\S+?)\}') + + +def _parse(spec_obj, context, line): + for tag in _tags: + match = tag.test(line) + if match: + return tag.update(spec_obj, context, match, line) + return spec_obj, context + + +class Requirement: + """Represents a single requirement or build requirement in an RPM spec file. + + Each spec file contains one or more requirements or build requirements. + For example, consider following spec file:: + + Name: foo + Version: 0.1 + + %description + %{name} is the library that everyone needs. + + %package devel + Summary: Header files, libraries and development documentation for %{name} + Group: Development/Libraries + Requires: %{name}%{?_isa} = %{version}-%{release} + BuildRequires: gstreamer%{?_isa} >= 0.1.0 + + %description devel + This package contains the header files, static libraries, and development + documentation for %{name}. If you like to develop programs using %{name}, you + will need to install %{name}-devel. + + This spec file's requirements have a name and either a required or minimum + version. + """ + expr = re.compile(r'(.*?)\s+([<>]=?|=)\s+(\S+)') + + def __init__(self, name): + assert isinstance(name, str) + self.line = name + match = Requirement.expr.match(name) + if match: + self.name = match.group(1) + self.operator = match.group(2) + self.version = match.group(3) + else: + self.name = name + self.operator = None + self.version = None + + def __repr__(self): + return self.line + + +class Package: + """Represents a single package in a RPM spec file. + + Each spec file describes at least one package and can contain one or more subpackages (described + by the %package directive). For example, consider following spec file:: + + Name: foo + Version: 0.1 + + %description + %{name} is the library that everyone needs. + + %package devel + Summary: Header files, libraries and development documentation for %{name} + Group: Development/Libraries + Requires: %{name}%{?_isa} = %{version}-%{release} + + %description devel + This package contains the header files, static libraries, and development + documentation for %{name}. If you like to develop programs using %{name}, you + will need to install %{name}-devel. + + %package -n bar + Summary: A command line client for foo. + License: GPLv2+ + + %description -n bar + This package contains a command line client for foo. + + This spec file will create three packages: + + * A package named foo, the base package. + * A package named foo-devel, a subpackage. + * A package named bar, also a subpackage, but without the foo- prefix. + + As you can see above, the name of a subpackage normally includes the main package name. When the + -n option is added to the %package directive, the prefix of the base package name is omitted and + a completely new name is used. + + """ + + def __init__(self, name): + assert isinstance(name, str) + + for tag in _tags: + if tag.attr_type is list and tag.name in ["build_requires", "requires", "conflicts", "obsoletes", + "provides"]: + setattr(self, tag.name, tag.attr_type()) + + self.name = name + self.is_subpackage = False + + def __repr__(self): + return "Package('{}')".format(self.name) + + +class Spec: + """Represents a single spec file. + + """ + + def __init__(self): + for tag in _tags: + if tag.attr_type is list: + setattr(self, tag.name, tag.attr_type()) + else: + setattr(self, tag.name, None) + + self.sources_dict = dict() + self.patches_dict = dict() + + @property + def packages_dict(self): + """All packages in this RPM spec as a dictionary. + + You can access the individual packages by their package name, e.g., + + git_spec.packages_dict['git-doc'] + + """ + assert self.packages + return dict(zip([package.name for package in self.packages], self.packages)) + + @staticmethod + def from_file(filename): + """Creates a new Spec object from a given file. + + :param filename: The path to the spec file. + :return: A new Spec object. + """ + + spec = Spec() + with open(filename, 'r', encoding='utf-8') as f: + parse_context = { + 'current_subpackage': None + } + for line in f: + spec, parse_context = _parse(spec, parse_context, line) + return spec + + @staticmethod + def from_string(string: str): + """Creates a new Spec object from a given string. + + :param string: The contents of a spec file. + :return: A new Spec object. + """ + + spec = Spec() + parse_context = { + 'current_subpackage': None + } + for line in string.splitlines(): + spec, parse_context = _parse(spec, parse_context, line) + return spec + + +def replace_macros(string, spec=None): + """Replace all macros in given string with corresponding values. + + For example: a string '%{name}-%{version}.tar.gz' will be transformed to 'foo-2.0.tar.gz'. + + :param string A string containing macros that you want to be replaced + :param spec An optional spec file. If given, definitions in that spec + file will be used to replace macros. + + :return A string where all macros in given input are substituted as good as possible. + + """ + if spec: + assert isinstance(spec, Spec) + + def _is_conditional(macro: str) -> bool: + return macro.startswith("?") or macro.startswith("!") + + def _test_conditional(macro: str) -> bool: + if macro[0] == "?": + return True + if macro[0] == "!": + return False + raise Exception("Given string is not a conditional macro") + + def _macro_repl(match): + macro_name = match.group(1) + if _is_conditional(macro_name) and spec: + parts = macro_name[1:].split(sep=":", maxsplit=1) + assert len(parts) > 0 + if _test_conditional(macro_name): # ? + if hasattr(spec, parts[0]): + if len(parts) == 2: + return parts[1] + else: + return getattr(spec, parts[0], None) + else: + return "" + else: # ! + if not hasattr(spec, parts[0]): + if len(parts) == 2: + return parts[1] + else: + return getattr(spec, parts[0], None) + else: + return "" + + if spec: + value = getattr(spec, macro_name, None) + if value: + return str(value) + return match.string[match.start():match.end()] + + # Recursively expand macros + # Note: If macros are not defined in the spec file, this won't try to + # expand them. + while True: + try: + ret = re.sub(_macro_pattern, _macro_repl, string) + if ret != string: + string = ret + continue + except: + ret = "" + finally: + return ret