1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3# Copyright (c) 2021 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import sys
17import argparse
18import os
19import shutil
20import stat
21
22sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
23from scripts.util.file_utils import read_json_file, write_json_file  # noqa: E402
24from scripts.util import build_utils  # noqa: E402
25
26README_FILE_NAME = 'README.OpenSource'
27LICENSE_CANDIDATES = [
28    'LICENSE',
29    'License',
30    'NOTICE',
31    'Notice',
32    'COPYRIGHT',
33    'Copyright',
34    'COPYING',
35    'Copying'
36]
37
38
39def is_top_dir(current_dir: str):
40    return os.path.exists(os.path.join(current_dir, '.gn'))
41
42
43def find_license_recursively(current_dir: str):
44    if is_top_dir(current_dir):
45        return None
46    for file in LICENSE_CANDIDATES:
47        candidate = os.path.join(current_dir, file)
48        if os.path.isfile(os.path.join(current_dir, file)):
49            return os.path.join(candidate)
50    return find_license_recursively(os.path.dirname(current_dir))
51
52
53def find_opensource_recursively(current_dir: str):
54    if is_top_dir(current_dir):
55        return None
56    candidate = os.path.join(current_dir, README_FILE_NAME)
57    if os.path.isfile(candidate):
58        return os.path.join(candidate)
59    return find_opensource_recursively(os.path.dirname(current_dir))
60
61
62def get_license_from_readme(readme_path: str):
63    contents = read_json_file(readme_path)
64    if contents is None:
65        raise Exception("Error: failed to read {}.".format(readme_path))
66
67    notice_file = contents[0].get('License File').strip()
68    notice_name = contents[0].get('Name').strip()
69    notice_version = contents[0].get('Version Number').strip()
70    if notice_file is None:
71        raise Exception("Error: value of notice file is empty in {}.".format(
72            readme_path))
73    if notice_name is None:
74        raise Exception("Error: Name of notice file is empty in {}.".format(
75            readme_path))
76    if notice_version is None:
77        raise Exception("Error: Version Number of notice file is empty in {}.".format(
78            readme_path))
79
80    return os.path.join(os.path.dirname(readme_path), notice_file), notice_name, notice_version
81
82
83def do_collect_notice_files(options, depfiles: str):
84    module_notice_info_list = []
85    module_notice_info = {}
86    notice_file = options.license_file
87    if notice_file:
88        opensource_file = find_opensource_recursively(os.path.abspath(options.module_source_dir))
89        if opensource_file is not None and os.path.exists(opensource_file):
90            notice_file_info = get_license_from_readme(opensource_file)
91            module_notice_info['Software'] = "{}".format(notice_file_info[1])
92            module_notice_info['Version'] = "{}".format(notice_file_info[2])
93        else:
94            module_notice_info['Software'] = ""
95            module_notice_info['Version'] = ""
96    if notice_file is None:
97        readme_path = os.path.join(options.module_source_dir,
98                                   README_FILE_NAME)
99        if not os.path.exists(readme_path):
100            readme_path = find_opensource_recursively(os.path.abspath(options.module_source_dir))
101        if readme_path is not None:
102            depfiles.append(readme_path)
103            notice_file_info = get_license_from_readme(readme_path)
104            notice_file = notice_file_info[0]
105            module_notice_info['Software'] = "{}".format(notice_file_info[1])
106            module_notice_info['Version'] = "{}".format(notice_file_info[2])
107
108    if notice_file is None:
109        notice_file = find_license_recursively(options.module_source_dir)
110        opensource_file = find_opensource_recursively(os.path.abspath(options.module_source_dir))
111        if opensource_file is not None and os.path.exists(opensource_file):
112            notice_file_info = get_license_from_readme(opensource_file)
113            module_notice_info['Software'] = "{}".format(notice_file_info[1])
114            module_notice_info['Version'] = "{}".format(notice_file_info[2])
115        else:
116            module_notice_info['Software'] = ""
117            module_notice_info['Version'] = ""
118
119    if module_notice_info['Software']:
120        module_notice_info['Path'] = "/{}".format(options.module_source_dir[5:])
121        module_notice_info_list.append(module_notice_info)
122
123    if notice_file:
124        for output in options.output:
125            notice_info_json = '{}.json'.format(output)
126            os.makedirs(os.path.dirname(output), exist_ok=True)
127            os.makedirs(os.path.dirname(notice_info_json), exist_ok=True)
128
129            notice_files = notice_file.split(',')
130            write_file_content(notice_files, options, output, notice_info_json, module_notice_info_list, depfiles)
131
132
133def write_file_content(notice_files, options, output, notice_info_json, module_notice_info_list, depfiles):
134    for notice_file in notice_files:
135        notice_file = notice_file.strip()
136        if not os.path.exists(notice_file):
137            notice_file = os.path.join(options.module_source_dir, notice_file)
138        if os.path.exists(notice_file):
139            if not os.path.exists(output):
140                build_utils.touch(output)
141            write_notice_to_output(notice_file, output)
142            write_json_file(notice_info_json, module_notice_info_list)
143        else:
144            build_utils.touch(output)
145            build_utils.touch(notice_info_json)
146        depfiles.append(notice_file)
147
148
149def write_notice_to_output(notice_file, output):
150    with os.fdopen(os.open(notice_file, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR),
151                   'r', encoding='utf-8', errors='ignore') as notice_data_flow:
152        license_content = notice_data_flow.read()
153    with os.fdopen(os.open(output, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR),
154                   'r', encoding='utf-8', errors='ignore') as output_data_flow:
155        output_file_content = output_data_flow.read()
156    if license_content not in output_file_content:
157        with os.fdopen(os.open(output, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR),
158                       'a', encoding='utf-8') as testfwk_info_file:
159            testfwk_info_file.write(f"{license_content}\n")
160            testfwk_info_file.close()
161
162
163def main(args):
164    args = build_utils.expand_file_args(args)
165
166    parser = argparse.ArgumentParser()
167    build_utils.add_depfile_option(parser)
168
169    parser.add_argument('--license-file', required=False)
170    parser.add_argument('--output', action='append', required=False)
171    parser.add_argument('--sources', action='append', required=False)
172    parser.add_argument('--sdk-install-info-file', required=False)
173    parser.add_argument('--label', required=False)
174    parser.add_argument('--sdk-notice-dir', required=False)
175    parser.add_argument('--module-source-dir',
176                        help='source directory of this module',
177                        required=True)
178
179    options = parser.parse_args()
180    depfiles = []
181
182    if options.sdk_install_info_file:
183        install_dir = ''
184        sdk_install_info = read_json_file(options.sdk_install_info_file)
185        for item in sdk_install_info:
186            if options.label == item.get('label'):
187                install_dir = item.get('install_dir')
188                break
189        if options.sources and install_dir:
190            for src in options.sources:
191                extend_output = os.path.join(options.sdk_notice_dir, install_dir,
192                                             '{}.{}'.format(os.path.basename(src), 'txt'))
193                options.output.append(extend_output)
194
195    do_collect_notice_files(options, depfiles)
196    if options.license_file:
197        depfiles.append(options.license_file)
198    build_utils.write_depfile(options.depfile, options.output[0], depfiles)
199
200
201if __name__ == '__main__':
202    sys.exit(main(sys.argv[1:]))
203