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