xref: /aosp_15_r20/external/pytorch/third_party/build_bundled.py (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1*da0073e9SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*da0073e9SAndroid Build Coastguard Workerimport argparse
3*da0073e9SAndroid Build Coastguard Workerimport os
4*da0073e9SAndroid Build Coastguard Worker
5*da0073e9SAndroid Build Coastguard Worker
6*da0073e9SAndroid Build Coastguard Workermydir = os.path.dirname(__file__)
7*da0073e9SAndroid Build Coastguard Workerlicenses = {'LICENSE', 'LICENSE.txt', 'LICENSE.rst', 'COPYING.BSD'}
8*da0073e9SAndroid Build Coastguard Worker
9*da0073e9SAndroid Build Coastguard Worker
10*da0073e9SAndroid Build Coastguard Workerdef collect_license(current):
11*da0073e9SAndroid Build Coastguard Worker    collected = {}
12*da0073e9SAndroid Build Coastguard Worker    for root, dirs, files in os.walk(current):
13*da0073e9SAndroid Build Coastguard Worker        license = list(licenses & set(files))
14*da0073e9SAndroid Build Coastguard Worker        if license:
15*da0073e9SAndroid Build Coastguard Worker            name = root.split('/')[-1]
16*da0073e9SAndroid Build Coastguard Worker            license_file = os.path.join(root, license[0])
17*da0073e9SAndroid Build Coastguard Worker            try:
18*da0073e9SAndroid Build Coastguard Worker                ident = identify_license(license_file)
19*da0073e9SAndroid Build Coastguard Worker            except ValueError:
20*da0073e9SAndroid Build Coastguard Worker                raise ValueError('could not identify license file '
21*da0073e9SAndroid Build Coastguard Worker                                 f'for {root}') from None
22*da0073e9SAndroid Build Coastguard Worker            val = {
23*da0073e9SAndroid Build Coastguard Worker                'Name': name,
24*da0073e9SAndroid Build Coastguard Worker                'Files': [root],
25*da0073e9SAndroid Build Coastguard Worker                'License': ident,
26*da0073e9SAndroid Build Coastguard Worker                'License_file': [license_file],
27*da0073e9SAndroid Build Coastguard Worker            }
28*da0073e9SAndroid Build Coastguard Worker            if name in collected:
29*da0073e9SAndroid Build Coastguard Worker                # Only add it if the license is different
30*da0073e9SAndroid Build Coastguard Worker                if collected[name]['License'] == ident:
31*da0073e9SAndroid Build Coastguard Worker                    collected[name]['Files'].append(root)
32*da0073e9SAndroid Build Coastguard Worker                    collected[name]['License_file'].append(license_file)
33*da0073e9SAndroid Build Coastguard Worker                else:
34*da0073e9SAndroid Build Coastguard Worker                    collected[name + f' ({root})'] = val
35*da0073e9SAndroid Build Coastguard Worker            else:
36*da0073e9SAndroid Build Coastguard Worker                collected[name] = val
37*da0073e9SAndroid Build Coastguard Worker    return collected
38*da0073e9SAndroid Build Coastguard Worker
39*da0073e9SAndroid Build Coastguard Worker
40*da0073e9SAndroid Build Coastguard Workerdef create_bundled(d, outstream, include_files=False):
41*da0073e9SAndroid Build Coastguard Worker    """Write the information to an open outstream"""
42*da0073e9SAndroid Build Coastguard Worker    collected = collect_license(d)
43*da0073e9SAndroid Build Coastguard Worker    sorted_keys = sorted(collected.keys())
44*da0073e9SAndroid Build Coastguard Worker    outstream.write('The PyTorch repository and source distributions bundle '
45*da0073e9SAndroid Build Coastguard Worker                    'several libraries that are \n')
46*da0073e9SAndroid Build Coastguard Worker    outstream.write('compatibly licensed.  We list these here.')
47*da0073e9SAndroid Build Coastguard Worker    files_to_include = []
48*da0073e9SAndroid Build Coastguard Worker    for k in sorted_keys:
49*da0073e9SAndroid Build Coastguard Worker        c = collected[k]
50*da0073e9SAndroid Build Coastguard Worker        files = ',\n     '.join(c['Files'])
51*da0073e9SAndroid Build Coastguard Worker        license_file = ',\n     '.join(c['License_file'])
52*da0073e9SAndroid Build Coastguard Worker        outstream.write('\n\n')
53*da0073e9SAndroid Build Coastguard Worker        outstream.write(f"Name: {c['Name']}\n")
54*da0073e9SAndroid Build Coastguard Worker        outstream.write(f"License: {c['License']}\n")
55*da0073e9SAndroid Build Coastguard Worker        outstream.write(f"Files: {files}\n")
56*da0073e9SAndroid Build Coastguard Worker        outstream.write('  For details, see')
57*da0073e9SAndroid Build Coastguard Worker        if include_files:
58*da0073e9SAndroid Build Coastguard Worker            outstream.write(' the files concatenated below: ')
59*da0073e9SAndroid Build Coastguard Worker            files_to_include += c['License_file']
60*da0073e9SAndroid Build Coastguard Worker        else:
61*da0073e9SAndroid Build Coastguard Worker            outstream.write(': ')
62*da0073e9SAndroid Build Coastguard Worker        outstream.write(license_file)
63*da0073e9SAndroid Build Coastguard Worker    for fname in files_to_include:
64*da0073e9SAndroid Build Coastguard Worker        outstream.write('\n\n')
65*da0073e9SAndroid Build Coastguard Worker        outstream.write(fname)
66*da0073e9SAndroid Build Coastguard Worker        outstream.write('\n' + '-' * len(fname) + '\n')
67*da0073e9SAndroid Build Coastguard Worker        with open(fname, 'r') as fid:
68*da0073e9SAndroid Build Coastguard Worker            outstream.write(fid.read())
69*da0073e9SAndroid Build Coastguard Worker
70*da0073e9SAndroid Build Coastguard Worker
71*da0073e9SAndroid Build Coastguard Workerdef identify_license(f, exception=''):
72*da0073e9SAndroid Build Coastguard Worker    """
73*da0073e9SAndroid Build Coastguard Worker    Read f and try to identify the license type
74*da0073e9SAndroid Build Coastguard Worker    This is __very__ rough and probably not legally binding, it is specific for
75*da0073e9SAndroid Build Coastguard Worker    this repo.
76*da0073e9SAndroid Build Coastguard Worker    """
77*da0073e9SAndroid Build Coastguard Worker    def squeeze(t):
78*da0073e9SAndroid Build Coastguard Worker        """Remove 'n and ' ', normalize quotes
79*da0073e9SAndroid Build Coastguard Worker        """
80*da0073e9SAndroid Build Coastguard Worker        t = t.replace('\n', '').replace(' ', '')
81*da0073e9SAndroid Build Coastguard Worker        t = t.replace('``', '"').replace("''", '"')
82*da0073e9SAndroid Build Coastguard Worker        return t
83*da0073e9SAndroid Build Coastguard Worker
84*da0073e9SAndroid Build Coastguard Worker    with open(f) as fid:
85*da0073e9SAndroid Build Coastguard Worker        txt = fid.read()
86*da0073e9SAndroid Build Coastguard Worker        if not exception and 'exception' in txt:
87*da0073e9SAndroid Build Coastguard Worker            license = identify_license(f, 'exception')
88*da0073e9SAndroid Build Coastguard Worker            return license + ' with exception'
89*da0073e9SAndroid Build Coastguard Worker        txt = squeeze(txt)
90*da0073e9SAndroid Build Coastguard Worker        if 'ApacheLicense' in txt:
91*da0073e9SAndroid Build Coastguard Worker            # Hmm, do we need to check the text?
92*da0073e9SAndroid Build Coastguard Worker            return 'Apache-2.0'
93*da0073e9SAndroid Build Coastguard Worker        elif 'MITLicense' in txt:
94*da0073e9SAndroid Build Coastguard Worker            # Hmm, do we need to check the text?
95*da0073e9SAndroid Build Coastguard Worker            return 'MIT'
96*da0073e9SAndroid Build Coastguard Worker        elif 'BSD-3-ClauseLicense' in txt:
97*da0073e9SAndroid Build Coastguard Worker            # Hmm, do we need to check the text?
98*da0073e9SAndroid Build Coastguard Worker            return 'BSD-3-Clause'
99*da0073e9SAndroid Build Coastguard Worker        elif 'BSD3-ClauseLicense' in txt:
100*da0073e9SAndroid Build Coastguard Worker            # Hmm, do we need to check the text?
101*da0073e9SAndroid Build Coastguard Worker            return 'BSD-3-Clause'
102*da0073e9SAndroid Build Coastguard Worker        elif 'BoostSoftwareLicense-Version1.0' in txt:
103*da0073e9SAndroid Build Coastguard Worker            # Hmm, do we need to check the text?
104*da0073e9SAndroid Build Coastguard Worker            return 'BSL-1.0'
105*da0073e9SAndroid Build Coastguard Worker        elif 'gettimeofday' in txt:
106*da0073e9SAndroid Build Coastguard Worker            # Used in opentelemetry-cpp/tools/vcpkg/ports/gettimeofday
107*da0073e9SAndroid Build Coastguard Worker            return 'Apache-2.0'
108*da0073e9SAndroid Build Coastguard Worker        elif 'libhungarian' in txt:
109*da0073e9SAndroid Build Coastguard Worker            # Used in opentelemetry-cpp/tools/vcpkg/ports/hungarian
110*da0073e9SAndroid Build Coastguard Worker            return 'Permissive (free to use)'
111*da0073e9SAndroid Build Coastguard Worker        elif 'PDCurses' in txt:
112*da0073e9SAndroid Build Coastguard Worker            # Used in opentelemetry-cpp/tools/vcpkg/ports/pdcurses
113*da0073e9SAndroid Build Coastguard Worker            return 'Public Domain for core'
114*da0073e9SAndroid Build Coastguard Worker        elif 'Copyright1999UniversityofNorthCarolina' in txt:
115*da0073e9SAndroid Build Coastguard Worker            # Used in opentelemetry-cpp/tools/vcpkg/ports/pqp
116*da0073e9SAndroid Build Coastguard Worker            return 'Apache-2.0'
117*da0073e9SAndroid Build Coastguard Worker        elif 'sigslot' in txt:
118*da0073e9SAndroid Build Coastguard Worker            # Used in opentelemetry-cpp/tools/vcpkg/ports/sigslot
119*da0073e9SAndroid Build Coastguard Worker            return 'Public Domain'
120*da0073e9SAndroid Build Coastguard Worker        elif squeeze("Clarified Artistic License") in txt:
121*da0073e9SAndroid Build Coastguard Worker            return 'Clarified Artistic License'
122*da0073e9SAndroid Build Coastguard Worker        elif all([squeeze(m) in txt.lower() for m in bsd3_txt]):
123*da0073e9SAndroid Build Coastguard Worker            return 'BSD-3-Clause'
124*da0073e9SAndroid Build Coastguard Worker        elif all([squeeze(m) in txt.lower() for m in bsd3_v1_txt]):
125*da0073e9SAndroid Build Coastguard Worker            return 'BSD-3-Clause'
126*da0073e9SAndroid Build Coastguard Worker        elif all([squeeze(m) in txt.lower() for m in bsd2_txt]):
127*da0073e9SAndroid Build Coastguard Worker            return 'BSD-2-Clause'
128*da0073e9SAndroid Build Coastguard Worker        elif all([squeeze(m) in txt.lower() for m in bsd3_src_txt]):
129*da0073e9SAndroid Build Coastguard Worker            return 'BSD-Source-Code'
130*da0073e9SAndroid Build Coastguard Worker        elif any([squeeze(m) in txt.lower() for m in mit_txt]):
131*da0073e9SAndroid Build Coastguard Worker            return 'MIT'
132*da0073e9SAndroid Build Coastguard Worker        else:
133*da0073e9SAndroid Build Coastguard Worker            raise ValueError('unknown license')
134*da0073e9SAndroid Build Coastguard Worker
135*da0073e9SAndroid Build Coastguard Workermit_txt = ['permission is hereby granted, free of charge, to any person ',
136*da0073e9SAndroid Build Coastguard Worker           'obtaining a copy of this software and associated documentation ',
137*da0073e9SAndroid Build Coastguard Worker           'files (the "software"), to deal in the software without ',
138*da0073e9SAndroid Build Coastguard Worker           'restriction, including without limitation the rights to use, copy, ',
139*da0073e9SAndroid Build Coastguard Worker           'modify, merge, publish, distribute, sublicense, and/or sell copies ',
140*da0073e9SAndroid Build Coastguard Worker           'of the software, and to permit persons to whom the software is ',
141*da0073e9SAndroid Build Coastguard Worker           'furnished to do so, subject to the following conditions:',
142*da0073e9SAndroid Build Coastguard Worker
143*da0073e9SAndroid Build Coastguard Worker           'the above copyright notice and this permission notice shall be ',
144*da0073e9SAndroid Build Coastguard Worker           'included in all copies or substantial portions of the software.',
145*da0073e9SAndroid Build Coastguard Worker
146*da0073e9SAndroid Build Coastguard Worker           'the software is provided "as is", without warranty of any kind, ',
147*da0073e9SAndroid Build Coastguard Worker           'express or implied, including but not limited to the warranties of ',
148*da0073e9SAndroid Build Coastguard Worker           'merchantability, fitness for a particular purpose and ',
149*da0073e9SAndroid Build Coastguard Worker           'noninfringement. in no event shall the authors or copyright holders ',
150*da0073e9SAndroid Build Coastguard Worker           'be liable for any claim, damages or other liability, whether in an ',
151*da0073e9SAndroid Build Coastguard Worker           'action of contract, tort or otherwise, arising from, out of or in ',
152*da0073e9SAndroid Build Coastguard Worker           'connection with the software or the use or other dealings in the ',
153*da0073e9SAndroid Build Coastguard Worker           'software.',
154*da0073e9SAndroid Build Coastguard Worker           ]
155*da0073e9SAndroid Build Coastguard Worker
156*da0073e9SAndroid Build Coastguard Workerbsd3_txt = ['redistribution and use in source and binary forms, with or without '
157*da0073e9SAndroid Build Coastguard Worker            'modification, are permitted provided that the following conditions '
158*da0073e9SAndroid Build Coastguard Worker            'are met:',
159*da0073e9SAndroid Build Coastguard Worker
160*da0073e9SAndroid Build Coastguard Worker            'redistributions of source code',
161*da0073e9SAndroid Build Coastguard Worker
162*da0073e9SAndroid Build Coastguard Worker            'redistributions in binary form',
163*da0073e9SAndroid Build Coastguard Worker
164*da0073e9SAndroid Build Coastguard Worker            'neither the name',
165*da0073e9SAndroid Build Coastguard Worker
166*da0073e9SAndroid Build Coastguard Worker            'this software is provided by the copyright holders and '
167*da0073e9SAndroid Build Coastguard Worker            'contributors "as is" and any express or implied warranties, '
168*da0073e9SAndroid Build Coastguard Worker            'including, but not limited to, the implied warranties of '
169*da0073e9SAndroid Build Coastguard Worker            'merchantability and fitness for a particular purpose are disclaimed.',
170*da0073e9SAndroid Build Coastguard Worker            ]
171*da0073e9SAndroid Build Coastguard Worker
172*da0073e9SAndroid Build Coastguard Worker# BSD2 is BSD3 without the "neither the name..." clause
173*da0073e9SAndroid Build Coastguard Workerbsd2_txt = bsd3_txt[:3] + bsd3_txt[4:]
174*da0073e9SAndroid Build Coastguard Worker
175*da0073e9SAndroid Build Coastguard Worker# This BSD3 variant leaves "and contributors" out of the last clause of BSD-3,
176*da0073e9SAndroid Build Coastguard Worker# which is still valid BSD-3
177*da0073e9SAndroid Build Coastguard Workerv1 = bsd3_txt[4].replace('and contributors', '')
178*da0073e9SAndroid Build Coastguard Workerbsd3_v1_txt = bsd3_txt[:3] + [v1]
179*da0073e9SAndroid Build Coastguard Worker
180*da0073e9SAndroid Build Coastguard Worker# This source variant of BSD-3 leaves the "redistributions in binary form" out
181*da0073e9SAndroid Build Coastguard Worker# which is https://spdx.org/licenses/BSD-Source-Code.html
182*da0073e9SAndroid Build Coastguard Workerbsd3_src_txt = bsd3_txt[:2] + bsd3_txt[4:]
183*da0073e9SAndroid Build Coastguard Worker
184*da0073e9SAndroid Build Coastguard Worker
185*da0073e9SAndroid Build Coastguard Workerif __name__ == '__main__':
186*da0073e9SAndroid Build Coastguard Worker    third_party = os.path.relpath(mydir)
187*da0073e9SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(
188*da0073e9SAndroid Build Coastguard Worker        description="Generate bundled licenses file",
189*da0073e9SAndroid Build Coastguard Worker    )
190*da0073e9SAndroid Build Coastguard Worker    parser.add_argument(
191*da0073e9SAndroid Build Coastguard Worker        "--out-file",
192*da0073e9SAndroid Build Coastguard Worker        type=str,
193*da0073e9SAndroid Build Coastguard Worker        default=os.environ.get(
194*da0073e9SAndroid Build Coastguard Worker            "PYTORCH_THIRD_PARTY_BUNDLED_LICENSE_FILE",
195*da0073e9SAndroid Build Coastguard Worker            str(os.path.join(third_party, 'LICENSES_BUNDLED.txt'))
196*da0073e9SAndroid Build Coastguard Worker        ),
197*da0073e9SAndroid Build Coastguard Worker        help="location to output new bundled licenses file",
198*da0073e9SAndroid Build Coastguard Worker    )
199*da0073e9SAndroid Build Coastguard Worker    parser.add_argument(
200*da0073e9SAndroid Build Coastguard Worker        "--include-files",
201*da0073e9SAndroid Build Coastguard Worker        action="store_true",
202*da0073e9SAndroid Build Coastguard Worker        default=False,
203*da0073e9SAndroid Build Coastguard Worker        help="include actual license terms to the output",
204*da0073e9SAndroid Build Coastguard Worker    )
205*da0073e9SAndroid Build Coastguard Worker    args = parser.parse_args()
206*da0073e9SAndroid Build Coastguard Worker    fname = args.out_file
207*da0073e9SAndroid Build Coastguard Worker    print(f"+ Writing bundled licenses to {args.out_file}")
208*da0073e9SAndroid Build Coastguard Worker    with open(fname, 'w') as fid:
209*da0073e9SAndroid Build Coastguard Worker        create_bundled(third_party, fid, args.include_files)
210