| #!/usr/bin/python |
| # |
| # Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| # |
| # Original Author: Brian Harring <ferringb@chromium.org> |
| # Maintainer: Marc MERLIN <merlin@chromium.org> |
| |
| """Cheesy script that attempts to generate an HTML file containing license |
| information and homepage links for all installed packages. |
| |
| This script is NOT ready for production, not style compliant, and is only |
| checked in as a placeholder until new logic from licenses.py can be moved |
| into it. |
| """ |
| |
| import cgi |
| import os |
| import subprocess |
| import sys |
| import shutil |
| |
| PKG_DATA_TARGET_DIR = '/usr/share/licenses' |
| |
| STOCK_LICENSE_DIRS = [ |
| os.path.expanduser('~/trunk/src/third_party/portage/licenses'), |
| 'licenses', |
| ] |
| |
| SKIPPED_CATEGORIES = [ |
| 'chromeos-base', |
| 'virtual', |
| ] |
| |
| SKIPPED_PACKAGES = [ |
| # These are Chrome-OS-specific packages. |
| 'media-libs/libresample', |
| 'sys-apps/rootdev', |
| 'sys-kernel/chromeos-kernel', # already manually credit Linux |
| |
| 'www-plugins/adobe-flash', |
| |
| # These have been split across several packages, so we skip listing the |
| # individual components (and just list the main package instead). |
| 'app-editors/vim-core', |
| 'x11-apps/mesa-progs', |
| |
| # Portage metapackage. |
| 'x11-base/xorg-drivers', |
| |
| # These are covered by app-i18n/ibus-mozc (BSD, copyright Google). |
| 'app-i18n/ibus-mozc-chewing', |
| 'app-i18n/ibus-mozc-hangul', |
| |
| # These are all X.org sub-packages; shouldn't be any need to list them |
| # individually. |
| 'media-fonts/encodings', |
| 'x11-apps/iceauth', |
| 'x11-apps/intel-gpu-tools', |
| 'x11-apps/mkfontdir', |
| 'x11-apps/rgb', |
| 'x11-apps/setxkbmap', |
| 'x11-apps/xauth', |
| 'x11-apps/xcursorgen', |
| 'x11-apps/xdpyinfo', |
| 'x11-apps/xdriinfo', |
| 'x11-apps/xev', |
| 'x11-apps/xgamma', |
| 'x11-apps/xhost', |
| 'x11-apps/xinit', |
| 'x11-apps/xinput', |
| 'x11-apps/xkbcomp', |
| 'x11-apps/xlsatoms', |
| 'x11-apps/xlsclients', |
| 'x11-apps/xmodmap', |
| 'x11-apps/xprop', |
| 'x11-apps/xrandr', |
| 'x11-apps/xrdb', |
| 'x11-apps/xset', |
| 'x11-apps/xwininfo', |
| 'x11-base/xorg-server', |
| 'x11-drivers/xf86-input-evdev', |
| 'x11-drivers/xf86-input-keyboard', |
| 'x11-drivers/xf86-input-mouse', |
| 'x11-drivers/xf86-input-synaptics', |
| 'x11-drivers/xf86-video-intel', |
| 'x11-drivers/xf86-video-vesa', |
| 'x11-drivers/xf86-video-vmware', |
| 'x11-libs/libICE', |
| 'x11-libs/libSM', |
| 'x11-libs/libX11', |
| 'x11-libs/libXScrnSaver', |
| 'x11-libs/libXau', |
| 'x11-libs/libXcomposite', |
| 'x11-libs/libXcursor', |
| 'x11-libs/libXdamage', |
| 'x11-libs/libXdmcp', |
| 'x11-libs/libXext', |
| 'x11-libs/libXfixes', |
| 'x11-libs/libXfont', |
| 'x11-libs/libXfontcache', |
| 'x11-libs/libXft', |
| 'x11-libs/libXi', |
| 'x11-libs/libXinerama', |
| 'x11-libs/libXmu', |
| 'x11-libs/libXp', |
| 'x11-libs/libXrandr', |
| 'x11-libs/libXrender', |
| 'x11-libs/libXres', |
| 'x11-libs/libXt', |
| 'x11-libs/libXtst', |
| 'x11-libs/libXv', |
| 'x11-libs/libXvMC', |
| 'x11-libs/libXxf86vm', |
| 'x11-libs/libdrm', |
| 'x11-libs/libfontenc', |
| 'x11-libs/libpciaccess', |
| 'x11-libs/libxkbfile', |
| 'x11-libs/libxkbui', |
| 'x11-libs/pixman', |
| 'x11-libs/xtrans', |
| 'x11-misc/util-macros', |
| 'x11-misc/xbitmaps', |
| 'x11-proto/bigreqsproto', |
| 'x11-proto/compositeproto', |
| 'x11-proto/damageproto', |
| 'x11-proto/dri2proto', |
| 'x11-proto/fixesproto', |
| 'x11-proto/fontcacheproto', |
| 'x11-proto/fontsproto', |
| 'x11-proto/inputproto', |
| 'x11-proto/kbproto', |
| 'x11-proto/printproto', |
| 'x11-proto/randrproto', |
| 'x11-proto/recordproto', |
| 'x11-proto/renderproto', |
| 'x11-proto/resourceproto', |
| 'x11-proto/scrnsaverproto', |
| 'x11-proto/trapproto', |
| 'x11-proto/videoproto', |
| 'x11-proto/xcmiscproto', |
| 'x11-proto/xextproto', |
| 'x11-proto/xf86bigfontproto', |
| 'x11-proto/xf86dgaproto', |
| 'x11-proto/xf86driproto', |
| 'x11-proto/xf86rushproto', |
| 'x11-proto/xf86vidmodeproto', |
| 'x11-proto/xineramaproto', |
| 'x11-proto/xproto', |
| ] |
| |
| LICENSE_FILENAMES = [ |
| 'COPYING', |
| 'COPYRIGHT', # used by strace |
| 'LICENCE', # used by openssh |
| 'LICENSE', |
| 'LICENSE.txt', # used by NumPy, glew |
| 'LICENSE.TXT', # used by hdparm |
| 'License', # used by libcap |
| 'IPA_Font_License_Agreement_v1.0.txt', # used by ja-ipafonts |
| ] |
| LICENSE_FILENAMES = frozenset(x.lower() for x in LICENSE_FILENAMES) |
| |
| SKIPPED_LICENSE_DIRECTORIES = frozenset([ |
| 'third_party' |
| ]) |
| |
| PACKAGE_LICENSES = { |
| 'app-crypt/nss': ['MPL-1.1'], |
| 'app-editors/gentoo-editor': ['MIT-gentoo-editor'], |
| 'app-editors/vim': ['vim'], |
| 'app-i18n/ibus-mozc': ['BSD-Google'], |
| 'dev-db/sqlite': ['sqlite'], |
| 'dev-libs/libevent': ['BSD-libevent'], |
| 'dev-libs/nspr': ['GPL-2'], |
| 'dev-libs/nss': ['GPL-2'], |
| 'dev-libs/protobuf': ['BSD-Google'], |
| 'dev-util/bsdiff': ['BSD-bsdiff'], |
| 'media-fonts/font-util': ['font-util'], # COPYING file from git repo |
| 'media-libs/freeimage': ['GPL-2'], |
| 'media-libs/jpeg': ['jpeg'], |
| 'media-plugins/o3d': ['BSD-Google'], |
| 'net-dialup/ppp': ['ppp-2.4.4'], |
| 'net-dns/c-ares': ['MIT-MIT'], |
| 'net-misc/dhcpcd': ['BSD-dhcpcd'], |
| 'net-misc/iputils': ['BSD-iputils'], |
| 'net-wireless/wpa_supplicant': ['GPL-2'], |
| 'net-wireless/iwl1000-ucode': ['Intel-iwl1000'], |
| 'net-wireless/marvell_sd8787': ['Marvell'], |
| 'sys-apps/less': ['BSD-less'], |
| 'sys-libs/ncurses': ['ncurses'], |
| 'sys-libs/talloc': ['LGPL-3'], # ebuild incorrectly says GPL-3 |
| 'sys-libs/timezone-data': ['public-domain'], |
| 'sys-process/vixie-cron': ['vixie-cron'], |
| 'x11-drivers/xf86-input-cmt': ['BSD-Google'], |
| } |
| |
| INVALID_STOCK_LICENSES = frozenset([ |
| 'BSD', # requires distribution of copyright notice |
| 'BSD-2', # same |
| 'BSD-4', # same |
| 'MIT', # same |
| ]) |
| |
| TEMPLATE_FILE = 'about_credits.tmpl' |
| ENTRY_TEMPLATE_FILE = os.path.dirname(os.path.abspath(__file__)) |
| ENTRY_TEMPLATE_FILE = os.path.join(ENTRY_TEMPLATE_FILE, |
| 'about_credits_entry.tmpl') |
| |
| def FilterUsableLicenses(raw_license_names): |
| for license_name in raw_license_names: |
| if license_name in INVALID_STOCK_LICENSES: |
| print 'skipping invalid stock license %s' % license_name |
| continue |
| yield license_name |
| |
| def _FindLicenseInSource(workdir): |
| names = [] |
| for name in LICENSE_FILENAMES: |
| if names: |
| names.append('-o') |
| names.extend(('-iname', name)) |
| if names: |
| names = ['('] + names + [')'] |
| |
| # ensure the directory has a trailing slash, else if it's a symlink |
| # find will just stop at the sym. |
| args = ['find', workdir +'/', '-maxdepth', '3', '-mindepth', '1', |
| '-type', 'f'] |
| args += names |
| names = [] |
| for dirname in SKIPPED_LICENSE_DIRECTORIES: |
| if names: |
| names.append('-o') |
| names.extend(('-ipath', '*/%s/*' % dirname)) |
| if names: |
| args += ['-a', '!', '('] + names + [')'] |
| p = subprocess.Popen(args, stdin=None, stderr=None, |
| stdout=subprocess.PIPE, shell=False) |
| stdout, _ = p.communicate() |
| files = filter(None, stdout.split("\n")) |
| if files: |
| return open(files[0], 'r').read() |
| return None |
| |
| def _GetStockLicense(licenses, stock_licenses): |
| if not licenses: |
| return None |
| |
| license_texts = [] |
| for license_name in licenses: |
| license_path = stock_licenses.get(license_name) |
| if license_path is None: |
| # TODO: We should probably report failure if we're unable to |
| # find one of the licenses from a dual-licensed package. |
| continue |
| license_texts.append(open(license_path).read()) |
| |
| return '\n'.join(license_texts) |
| |
| def identifyLicenseText(workdir, metadata_licenses, stock_licenses): |
| license_text = _FindLicenseInSource(workdir) |
| if not license_text: |
| license_text = _GetStockLicense(metadata_licenses, stock_licenses) |
| if not license_text: |
| raise Exception("failed finding a license in both the source, and a " |
| "usable stock license") |
| return license_text |
| |
| def EvaluateTemplate(template, env, escape=True): |
| """Expand |template| with content like {{foo}} using a dict of expansions.""" |
| for key, val in env.items(): |
| if escape: |
| val = cgi.escape(val) |
| template = template.replace('{{%s}}' % key, val) |
| return template |
| |
| def WritePkgData(image_dir, pkgname, license_text, homepage): |
| data = EvaluateTemplate(open(ENTRY_TEMPLATE_FILE).read(), |
| dict(name=pkgname, license=license_text, url=homepage)) |
| base = os.path.join(image_dir, PKG_DATA_TARGET_DIR.lstrip('/'), pkgname) |
| shutil.rmtree(base, ignore_errors=True) |
| os.makedirs(base, 0o755) |
| with open(os.path.join(base, 'license_text'), 'w') as f: |
| f.write(data) |
| |
| def main(fullname, license_data, homepage_data, image_dir, workdir, |
| stock_license_dir, forced_license_files=None): |
| metadata_licenses = list(FilterUsableLicenses(license_data.split())) |
| |
| stock_licenses = {} |
| # ordering matters; via this, our license texts override the gentoo |
| # provided ones where desired. |
| our_license_dir = os.path.dirname(os.path.abspath(__file__)) |
| our_license_dir = os.path.join(our_license_dir, 'licenses') |
| for license_dir in [stock_license_dir, our_license_dir]: |
| stock_licenses.update((x, os.path.join(license_dir, x)) |
| for x in os.listdir(license_dir) |
| if x not in INVALID_STOCK_LICENSES) |
| |
| if forced_license_files: |
| licenses_text = "\n".join(open(os.path.join(workdir, x)).read() |
| for x in forced_license_files.split()) |
| else: |
| licenses_text = identifyLicenseText(workdir, metadata_licenses, |
| stock_licenses) |
| |
| homepages = homepage_data.split() |
| if not homepages: |
| homepage = '' |
| else: |
| homepage = homepages[0] |
| |
| if not licenses_text: |
| raise Exception("failed identifying license text for %s" % fullname) |
| |
| WritePkgData(image_dir, fullname, licenses_text, homepage) |
| |
| if __name__ == '__main__': |
| # ./process-pkg.py "${CATEGORY}/${PN}-${PVR}" "${LICENSE}" "${HOMEPAGE}" |
| # "${D}" "${WORKDIR}" |
| # "${LICENSES_DIR}" |
| if len(sys.argv) < 7: |
| sys.stderr.write("not enough args; expected ${CATEGORY}/${PN}-${PVR} " |
| "${LICENSE} ${HOMEPAGE} ${D} ${WORKDIR} ${PORTDIR}/licenses" |
| "[FORCED_LICENSE_FILE1 .. FORCED_LICENSE_FILE9]\n" |
| "got %i args: %r\n" |
| % (len(sys.argv) -1, sys.argv)) |
| sys.exit(1) |
| args = sys.argv[1:7] |
| forced_license_files = sys.argv[7:] |
| main(forced_license_files=forced_license_files, *sys.argv[1:]) |
| sys.exit(0) |