From patchwork Mon Sep 28 07:46:34 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Packham X-Patchwork-Id: 523257 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by ozlabs.org (Postfix) with ESMTP id 1B4061401CD for ; Mon, 28 Sep 2015 17:47:00 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=wETe3Aqo; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id 618FB8958D; Mon, 28 Sep 2015 07:46:59 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id O3rtIUW9q56C; Mon, 28 Sep 2015 07:46:58 +0000 (UTC) Received: from ash.osuosl.org (ash.osuosl.org [140.211.166.34]) by hemlock.osuosl.org (Postfix) with ESMTP id 33135892DB; Mon, 28 Sep 2015 07:46:58 +0000 (UTC) X-Original-To: buildroot@lists.busybox.net Delivered-To: buildroot@osuosl.org Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by ash.osuosl.org (Postfix) with ESMTP id CC37B1C1F9C for ; Mon, 28 Sep 2015 07:46:56 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id C73DE25DEC for ; Mon, 28 Sep 2015 07:46:56 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id V79TGkyjqQbz for ; Mon, 28 Sep 2015 07:46:54 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mail-pa0-f51.google.com (mail-pa0-f51.google.com [209.85.220.51]) by silver.osuosl.org (Postfix) with ESMTPS id 8691C2542D for ; Mon, 28 Sep 2015 07:46:54 +0000 (UTC) Received: by pacfv12 with SMTP id fv12so170779249pac.2 for ; Mon, 28 Sep 2015 00:46:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=UtjcOI/9Bogm95IAlXE4zklS9PN0UMTg6WhAkc2hoXM=; b=wETe3AqokUk6hmBpQBDtSPXoyA9t9+wmoVTPRVnPf+o5wnQ1K7I0fp/GIgsi73Banx az0Bx1pT9R5c8j0ca0sFYye6TLq4pTSGQG+mDNnRkU1ODmLIipCf1LSoMHvwDhzKoPhX YdoRi+JJ33DvYiorTFyZdz8fDm0egN8cES9Y2J3zfHP6lo9EnuO/MOFnl+01ARWMmmEL EBpIiu38Sw1GZXc5vJbCkmNVTYU/uPdw/BzGalQFq610I+ISt5k8zg37MmmHGDKAjhcN dIxRd+JPm9aa6aLdluLTEopfGpShReb/HG77cPLdNfDQBz6Ygd9PlewZvqAATRfkfBu/ nlIA== X-Received: by 10.68.68.233 with SMTP id z9mr25110778pbt.132.1443426414159; Mon, 28 Sep 2015 00:46:54 -0700 (PDT) Received: from chrisp-dl.ws.atlnz.lc (2-163-36-202-static.alliedtelesis.co.nz. [202.36.163.2]) by smtp.gmail.com with ESMTPSA id rx8sm17511777pbb.90.2015.09.28.00.46.51 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 28 Sep 2015 00:46:53 -0700 (PDT) From: Chris Packham To: buildroot@buildroot.org Date: Mon, 28 Sep 2015 20:46:34 +1300 Message-Id: <1443426394-13595-2-git-send-email-judge.packham@gmail.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1443426394-13595-1-git-send-email-judge.packham@gmail.com> References: <1443426394-13595-1-git-send-email-judge.packham@gmail.com> Subject: [Buildroot] [RFC PATCH 1/1] scripts: Add gen-package.py X-BeenThere: buildroot@busybox.net X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: Discussion and development of buildroot List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: buildroot-bounces@busybox.net Sender: "buildroot" gen-package.py can be used to generate the initial skeleton Config.in and .mk for a package based on its download URL. Signed-off-by: Chris Packham --- support/scripts/gen-package.py | 266 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100755 support/scripts/gen-package.py diff --git a/support/scripts/gen-package.py b/support/scripts/gen-package.py new file mode 100755 index 0000000..d7b126a --- /dev/null +++ b/support/scripts/gen-package.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python + +## gen-package.py +## +## This script generates the basic skeleton for a new package +## +## Copyright (C) 2015 Chris Packham +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +## MA 02110-1301, USA. +## +""" +Given the download URL for a package generate Config.in and .mk. This +automates some of the process but does not completely catch all of the +information that needs to be provided so some manual intervention is still +required. +""" +import argparse +import errno +import os +import re +import shutil + + +class ConfigDotIn(object): + """ + Generator for Config.in file + """ + template = """\ +config BR2_PACKAGE_{NAME} +\tbool "{name}" +\tdepends on {depends} +\thelp +\t TODO: Description of {name} + +\t {home}/ +""" + + def __init__(self, info, depends): + self.info = info + pkgdeps = ['BR2_PACKAGE_{}'.format(x.upper()) for x in depends] + self.deps = '\n\tdepends on '.join(pkgdeps) + + def __str__(self): + elems = dict(self.info) + elems['depends'] = self.deps + return self.template.format(**elems) + + +class PackageMakefile(object): + """ + Generator for package makefile. This class should not be directly + instantiated instead one of its subclasses should be used to create a + makefile using the appropriate build infrastructure. + """ + template = """\ +################################################################################ +# +# {name} +# +################################################################################ + +{NAME}_VERSION = {version} +{NAME}_SOURCE = {name}{sep}$({NAME}_VERSION).{compression} +{NAME}_SITE = {site} +{NAME}_LICENSE = #TODO: add license abbreviation +{NAME}_LICENSE_FILES = COPYING LICENSE #TODO: update +{NAME}_DEPENDENCIES = {depends} + +$(eval $({type}-package)) +""" + + def __init__(self, info, depends): + self.info = info + site = self.info['site'] + site = site.replace(self.info['version'], + '$(BR2_{}_VERISON)'.format(self.info['NAME'])) + self.info['site'] = site + self.deps = " ".join(depends) + + def __str__(self): + elems = dict(self.info) + elems['depends'] = self.deps + return self.template.format(**elems) + + +class GenericPackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'generic' + super(self.__class__, self).__init__(info, depends) + + +class AutotoolsPackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'autotools' + super(self.__class__, self).__init__(info, depends) + + +class CmakePackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'cmake' + super(self.__class__, self).__init__(info, depends) + + +class PythonPackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'python' + super(self.__class__, self).__init__(info, depends) + + +class LuarocksPackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'luarocks' + super(self.__class__, self).__init__(info, depends) + + +class PerlPackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'perl' + super(self.__class__, self).__init__(info, depends) + + +class KconfigPackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'kconfig' + super(self.__class__, self).__init__(info, depends) + + +class RebarPackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'rebar' + super(self.__class__, self).__init__(info, depends) + + +class KernelModulePackage(PackageMakefile): + def __init__(self, info, depends): + info['type'] = 'kernel-module' + super(self.__class__, self).__init__(info, depends) + self.template += '$(eval $(generic-package))' + self.template = self.template.replace('{type}-package', + 'kernel-module') + + +def guess_package_info(url): + """ + Given the download URL for a package, make an educated guess at the + information needed to generate a buildroot package.mk and Config.in. + """ + pattern = '(?P\S+)(?P-|_)(?P\S+)\.(?Ptar\.(gz|bz2|xz)|tgz|zip)' + u = url.split('/') + tarball = u[-1] + site = '/'.join(u[:-1]) + home = '/'.join(u[0:3]) + + m = re.search(pattern, tarball) + if m: + return {'name': m.group('name'), + 'NAME': m.group('name').upper().replace('-', '_'), + 'sep': m.group('sep'), + 'version': m.group('version'), + 'compression': m.group('compression'), + 'site': site, + 'home': home} + else: + raise RuntimeError('Could not guess package info from {}'.format(url)) + + +def mkdir(dir_): + """ + os.mkdir wrapper that handles the directory already existing (by not + caring). + """ + try: + os.mkdir(pkgdir) + except OSError as e: + if e.errno == errno.EEXIST: + pass + else: + raise e + +post = """ +What next? + +Add {config} to package/Config.in. + +Review {makefile} and {config} +against the instructions for adding a new package[1]. This script has added +some TODO markers for things that can't be detected automatically. + +Update {config} to fill in the license details[2]. + +Test that the new package builds for a few different architectures. + +Create a patch for buildroot and send it to the mailinglist[3]. + +[1] http://buildroot.org/downloads/manual/manual.html#adding-packages +[2] http://buildroot.org/downloads/manual/manual.html#legal-info-list-licenses +[3] http://buildroot.org/contribute.html +""" + + +if __name__ == '__main__': + packages = {'autotools': AutotoolsPackage, + 'generic': GenericPackage, + 'cmake': CmakePackage, + 'python': PythonPackage, + 'luarocks': LuarocksPackage, + 'perl': PerlPackage, + 'kconfig': KconfigPackage, + 'rebar': RebarPackage, + 'kernel-module': KernelModulePackage} + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('-b', '--buildroot', type=str, default=os.getcwd(), + help='buildroot source dir (default: %(default)s)') + parser.add_argument('-t', '--type', type=str, default='autotools', + choices=packages.keys(), metavar='TYPE', + help='package type one of: %(choices)s') + parser.add_argument('-n', '--dry-run', default=False, action='store_true', + help='dry-run (don\'t create files)') + parser.add_argument('url', type=str, help='URL of the package source') + parser.add_argument('depends', type=str, nargs='*', default=[], + help="names of other packages that this depends on") + + args = parser.parse_args() + info = guess_package_info(args.url) + + pkgdir = os.path.join(args.buildroot, 'package', info['name']) + config_in = os.path.join(pkgdir, 'Config.in') + makefile = os.path.join(pkgdir, info['name']+'.mk') + + if args.dry_run: + print('(dry-run) {}'.format(config_in)) + print(ConfigDotIn(info, args.depends)) + print('(dry-run) {}'.format(makefile)) + print(packages[args.type](info, args.depends)) + else: + mkdir(pkgdir) + + if os.path.exists(config_in): + shutil.copyfile(config_in, config_in+'~') + if os.path.exists(makefile): + shutil.copyfile(makefile, makefile+'~') + + with open(config_in, 'w') as f: + print("Creating {}".format(config_in)) + f.write(str(ConfigDotIn(info, args.depends))) + with open(makefile, 'w') as f: + print("Creating {}".format(makefile)) + f.write(str(packages[args.type](info, args.depends))) + + print(post.format(config=config_in.replace(args.buildroot+'/', ''), + makefile=makefile.replace(args.buildroot+'/', ''), + name=info['name']))