summaryrefslogtreecommitdiff
path: root/pkgtools/url2pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkgtools/url2pkg')
-rw-r--r--pkgtools/url2pkg/Makefile15
-rw-r--r--pkgtools/url2pkg/PLIST3
-rw-r--r--pkgtools/url2pkg/files/url2pkg.87
-rw-r--r--pkgtools/url2pkg/files/url2pkg.py859
-rw-r--r--pkgtools/url2pkg/files/url2pkg_test.py447
5 files changed, 1321 insertions, 10 deletions
diff --git a/pkgtools/url2pkg/Makefile b/pkgtools/url2pkg/Makefile
index a31bcd6cca8..30e8d8adf4e 100644
--- a/pkgtools/url2pkg/Makefile
+++ b/pkgtools/url2pkg/Makefile
@@ -1,6 +1,6 @@
-# $NetBSD: Makefile,v 1.102 2019/10/01 19:41:23 rillig Exp $
+# $NetBSD: Makefile,v 1.103 2019/10/03 09:37:40 rillig Exp $
-PKGNAME= url2pkg-19.3.0
+PKGNAME= url2pkg-19.3.1
CATEGORIES= pkgtools
MAINTAINER= rillig@NetBSD.org
@@ -15,24 +15,27 @@ USE_LANGUAGES= # none
USE_TOOLS+= perl:run
AUTO_MKDIRS= yes
+TEST_DEPENDS+= ${PYPKGPREFIX}-test>=0:../../devel/py-test
+
do-extract:
${RUN} cd ${FILESDIR} && cp *.* ${WRKSRC}/
do-test:
${RUN} cd ${WRKSRC} && env PKGSRCDIR=${PKGSRCDIR} perl -I. url2pkg.t
+ ${RUN} cd ${WRKSRC} && env PKGSRCDIR=${PKGSRCDIR} ${PREFIX}/bin/pytest-${PYVERSSUFFIX}
.include "../../mk/bsd.prefs.mk"
SUBST_CLASSES+= up
SUBST_STAGE.up= do-configure
SUBST_MESSAGE.up= Replacing variable placeholders
-SUBST_FILES.up= url2pkg.pl MakeMaker.pm
-SUBST_VARS.up= MAKE PERL5 PYTHONBIN
-SUBST_SED.up+= -e 's,@LIBDIR@,${PREFIX}/lib/url2pkg,g'
-SUBST_SED.up+= -e 's,@PKGSRCDIR@,${BATCH:D/usr/pkgsrc:U${PKGSRCDIR}},g'
+SUBST_FILES.up= url2pkg.pl MakeMaker.pm url2pkg.py
+SUBST_VARS.up= MAKE PERL5 PKGSRCDIR PYTHONBIN
+SUBST_SED.up= -e 's,@LIBDIR@,${PREFIX}/lib/url2pkg,g'
do-install:
${INSTALL_SCRIPT} ${WRKSRC}/url2pkg.pl ${DESTDIR}${PREFIX}/bin/url2pkg
+ ${INSTALL_SCRIPT} ${WRKSRC}/url2pkg.py ${DESTDIR}${PREFIX}/bin/url2pkg-py
${INSTALL_MAN} ${FILESDIR}/url2pkg.8 ${DESTDIR}${PREFIX}/${PKGMANDIR}/man8
${INSTALL_DATA} ${WRKSRC}/Build.pm ${DESTDIR}${PREFIX}/lib/url2pkg/Module/
${INSTALL_DATA} ${WRKSRC}/MakeMaker.pm ${DESTDIR}${PREFIX}/lib/url2pkg/ExtUtils/
diff --git a/pkgtools/url2pkg/PLIST b/pkgtools/url2pkg/PLIST
index 97ab4687cea..6d800bd334d 100644
--- a/pkgtools/url2pkg/PLIST
+++ b/pkgtools/url2pkg/PLIST
@@ -1,5 +1,6 @@
-@comment $NetBSD: PLIST,v 1.5 2019/09/12 18:23:00 rillig Exp $
+@comment $NetBSD: PLIST,v 1.6 2019/10/03 09:37:40 rillig Exp $
bin/url2pkg
+bin/url2pkg-py
lib/url2pkg/ExtUtils/MakeMaker.pm
lib/url2pkg/Module/Build.pm
lib/url2pkg/setuptools.py
diff --git a/pkgtools/url2pkg/files/url2pkg.8 b/pkgtools/url2pkg/files/url2pkg.8
index 8eef8671432..00dd5601ff0 100644
--- a/pkgtools/url2pkg/files/url2pkg.8
+++ b/pkgtools/url2pkg/files/url2pkg.8
@@ -1,6 +1,6 @@
-.\" $NetBSD: url2pkg.8,v 1.12 2018/08/22 20:48:38 maya Exp $
+.\" $NetBSD: url2pkg.8,v 1.13 2019/10/03 09:37:41 rillig Exp $
.\"
-.\" Copyright (c) 2001 The NetBSD Foundation, Inc.
+.\" Copyright (c) 2001, 2019 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
@@ -27,7 +27,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd May 29, 2013
+.Dd Oct 3, 2019
.Dt URL2PKG 8
.Os
.Sh NAME
@@ -35,6 +35,7 @@
.Nd Automatic pkgsrc package generator
.Sh SYNOPSIS
.Nm
+.Op Fl v|--verbose
.Op Ar URL
.Sh DESCRIPTION
.Nm
diff --git a/pkgtools/url2pkg/files/url2pkg.py b/pkgtools/url2pkg/files/url2pkg.py
new file mode 100644
index 00000000000..8b7a4be8f8d
--- /dev/null
+++ b/pkgtools/url2pkg/files/url2pkg.py
@@ -0,0 +1,859 @@
+#! @PYTHONBIN@
+# $NetBSD: url2pkg.py,v 1.1 2019/10/03 09:37:41 rillig Exp $
+
+# Copyright (c) 2019 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Roland Illig.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import getopt
+import glob
+import os
+import re
+import subprocess
+import sys
+from os.path import isfile
+from typing import Callable, Dict, Iterator, List, Optional, Sequence, Union
+
+
+def cvsid(fmt: str) -> str:
+ return fmt % ('$' + 'NetBSD$')
+
+
+class Config:
+
+ def __init__(self):
+ self.make = '@MAKE@'
+ self.libdir = '@LIBDIR@'
+ self.perl5 = '@PERL5@'
+ self.pkgsrcdir = '@PKGSRCDIR@'
+ self.pythonbin = '@PYTHONBIN@'
+ self.verbose = False
+
+
+config = Config()
+distname = ''
+
+
+def debug(fmt: str, *args):
+ if config.verbose:
+ msg = fmt % map(repr, args)
+ sys.stderr.write('url2pkg: %s\n' % msg)
+
+
+def run_editor(fname: str, lineno: int):
+ editor = os.getenv('PKGEDITOR') or os.getenv('EDITOR') or 'vi'
+
+ args: List[str] = [editor]
+ if re.search(editor, r'(^|/)(mcedit|nano|pico|vi|vim)$'):
+ args.append(f'+{lineno}')
+ args.append(fname)
+
+ code = subprocess.check_call(args)
+
+
+def generate_initial_package_Makefile_lines(url):
+ global distname
+
+ master_site = ''
+ master_sites = ''
+ distfile = ''
+ homepage = ''
+ extract_sufx = ''
+ categories = ''
+ github_project = ''
+ github_release = ''
+ dist_subdir = ''
+ pkgname_prefix = ''
+ pkgname_transform = ''
+
+ with open('../../mk/fetch/sites.mk') as sites_mk:
+ for line in sites_mk:
+ m = re.search(r'^(MASTER_SITE_.*)\+=', line)
+ if m:
+ master_site = m[1]
+ continue
+
+ m = re.search(r'^\t(.*?)(?:\s+\\)?$', line)
+ if not m:
+ continue
+
+ site = m[1]
+ if not url.startswith(site):
+ continue
+
+ rest = url[len(site):]
+ m = re.search(r'^(.+)/([^/]+)$', rest)
+ if not m:
+ master_sites = "${%s}" % master_site
+ continue
+
+ subdir, distfile = m.groups()
+
+ master_sites = '${%s:=%s/}' % (master_site, subdir)
+ if master_site == 'MASTER_SITE_SOURCEFORGE':
+ homepage = f'https://{subdir}.sourceforge.net/'
+ elif master_site == 'MASTER_SITE_GNU':
+ homepage = f'https://www.gnu.org/software/{subdir}/'
+ else:
+ homepage = url[:-len(distfile)]
+
+ m = re.search(r'^https://downloads\.sourceforge\.net/project/([^/?]+)/[^?]+/([^/?]+)(?:[?].*)?$', url)
+ if m:
+ project, filename = m.groups()
+
+ master_sites = '${MASTER_SITE_SOURCEFORGE:=%s/}' % project
+ homepage = 'https://%s.sourceforge.net/' % project
+ distfile = filename
+
+ m = re.search(r'^https://github\.com/(.+)/(.+)/archive/(.+)(\.tar\.gz|\.zip)$', url)
+ if m:
+ org, proj, tag, ext = m.groups()
+
+ github_project = proj
+ master_sites = '${MASTER_SITE_GITHUB:=%s/}' % org
+ homepage = 'https://github.com/%s/%s/' % (org, proj)
+ if github_project not in tag:
+ pkgname_prefix = '${GITHUB_PROJECT}-'
+ dist_subdir = '${GITHUB_PROJECT}'
+ distfile = tag + ext
+
+ m = re.search(r'^https://github\.com/(.+)/(.+)/releases/download/(.+)/(.+)(\.tar\.gz|\.zip)$', url)
+ if m:
+ org, proj, tag, base, ext = m.groups()
+
+ github_project = proj
+ master_sites = '${MASTER_SITE_GITHUB:=%s/}' % org
+ homepage = 'https://github.com/%s/%s/' % (org, proj)
+ if proj not in base:
+ github_project = proj
+ dist_subdir = '${GITHUB_PROJECT}'
+ github_release = '${DISTNAME}' if tag == base else tag
+ distfile = base + ext
+
+ if master_sites == '':
+ m = re.search(r'^(.*/)(.*)$', url)
+ master_sites = m[1]
+ distfile = m[2]
+ homepage = master_sites
+
+ m = re.search(r'^(.*?)((?:\.tar)?\.\w+)$', distfile)
+ if m:
+ distname, extract_sufx = m.groups()
+ else:
+ distname, extract_sufx = distfile, '# none'
+
+ m = re.search(r'^v\d', distname)
+ if m:
+ pkgname_transform = ':S,^v,,'
+ elif re.search(r'-v\d', distname) and not re.search(r'-v.*-v\d', distname):
+ pkgname_transform = ':S,-v,-,'
+
+ main_category = re.search(r'.*/([^/]+)/[^/]+$', os.getcwd())[1]
+
+ categories = main_category if main_category != 'wip' else '# TODO: add primary category'
+
+ if extract_sufx == '.tar.gz' or extract_sufx == '.gem':
+ extract_sufx = ''
+
+ pkgname = '%s${DISTNAME%s}' % (pkgname_prefix, pkgname_transform)
+ if pkgname == '${DISTNAME}':
+ pkgname = ''
+
+ maintainer = \
+ os.getenv('PKGMAINTAINER') or os.getenv('REPLYTO') \
+ or 'INSERT_YOUR_MAIL_ADDRESS_HERE'
+
+ lines = Lines()
+ lines.add(cvsid('# %s'))
+ lines.add('')
+
+ lines.add_vars(
+ Var('GITHUB_PROJECT', '=', github_project),
+ Var('DISTNAME', '=', distname),
+ Var('PKGNAME', '=', pkgname),
+ Var('CATEGORIES', '=', categories),
+ Var('MASTER_SITES', '=', master_sites),
+ Var('GITHUB_RELEASE', '=', github_release),
+ Var('EXTRACT_SUFX', '=', extract_sufx),
+ Var('DIST_SUBDIR', '=', dist_subdir),
+ )
+
+ lines.add_vars(
+ Var('MAINTAINER', '=', maintainer),
+ Var('HOMEPAGE', '=', homepage),
+ Var('COMMENT', '=', 'TODO: Short description of the package'),
+ Var('#LICENSE', '=', '# TODO: (see mk/license.mk)'),
+ )
+
+ lines.add('# url2pkg-marker (please do not remove this line.)')
+ lines.add('.include "../../mk/bsd.pkg.mk"')
+
+ return lines
+
+
+def generate_initial_package(url):
+ try:
+ os.rename('Makefile', 'Makefile-url2pkg.bak')
+ except OSError:
+ pass
+ generate_initial_package_Makefile_lines(url).write_to('Makefile')
+ Lines(cvsid('@comment %s')).write_to('PLIST')
+ Lines().write_to('DESCR')
+ run_editor('Makefile', 5)
+
+ bmake('distinfo')
+ bmake('extract')
+
+
+class Var:
+ """ A variable assignment for the package Makefile """
+
+ def __init__(self, name: str, op: str, value: str):
+ self.name = name
+ self.op = op
+ self.value = value
+
+
+def aligned(vars: List[Var]) -> List[str]:
+ relevant = list(filter(lambda v: v.value != '', vars))
+ if not relevant:
+ return []
+
+ def tabwidth(var: Var) -> int:
+ return (len(var.name) + len(var.op) + len('\t') + 7) // 8 * 8
+
+ width = max(map(tabwidth, relevant), default=0)
+
+ aligned_lines = []
+ for var in relevant:
+ tabs = '\t' * ((width - len(var.name) - len(var.op) + 7) // 8)
+ aligned_lines.append(var.name + var.op + tabs + var.value)
+
+ aligned_lines.append('')
+ return aligned_lines
+
+
+class Varassign:
+
+ def __init__(self, index: int, varname: str, op: str, indent: str,
+ value: str, space_after_value: str, comment: str):
+ self.index = index
+ self.varname = varname
+ self.op = op
+ self.indent = indent
+ self.value = value
+ self.space_after_value = space_after_value
+ self.comment = comment
+
+
+def find_package(pkgbase: str) -> str:
+ candidates = glob.glob(config.pkgsrcdir + '/*/' + pkgbase)
+ debug('candidates for package %s are %s', pkgbase, candidates)
+ if len(candidates) != 1:
+ return ''
+ return candidates[0].replace(config.pkgsrcdir, '../..')
+
+
+def bmake(*args: str) -> None:
+ debug('running bmake %s', args)
+ subprocess.check_call([config.make] + list(args))
+
+
+def show_var(varname: str) -> str:
+ output = subprocess.check_output((config.make, 'show-var', 'VARNAME=' + varname))
+ return output.decode('utf-8').strip()
+
+
+class Lines:
+
+ def __init__(self, *lines: str) -> None:
+ self.lines = []
+ for line in lines:
+ self.add(line)
+
+ @classmethod
+ def read_from(cls, filename: str) -> 'Lines':
+ pass
+
+ lines = Lines()
+ with open(filename) as f:
+ for line in f:
+ lines.add(line.rstrip('\n'))
+ return lines
+
+ def write_to(self, filename: str):
+ with open(f'{filename}.tmp', 'w') as f:
+ f.writelines(line + '\n' for line in self.lines)
+ try:
+ os.remove(filename)
+ except OSError:
+ pass
+ os.rename(f'{filename}.tmp', filename)
+
+ def add(self, *lines: Sequence[str]):
+ for line in lines:
+ assert type(line) == str, type(line)
+ self.lines.append(line)
+
+ def add_vars(self, *vars: Var):
+ """
+ Appends the given variable assignments to the lines, aligning the
+ variable values vertically.
+ """
+ width = 0
+ for var in vars:
+ if var.value != '':
+ name_op_len = (len(var.name) + len(var.op) + len('\t') + 7) // 8 * 8
+ width = max(width, name_op_len)
+
+ if width == 0:
+ return
+
+ for var in vars:
+ if var.value != '':
+ tabs = (width - len(var.name) - len(var.op) + 7) // 8
+ self.add(var.name + var.op + '\t' * tabs + var.value)
+ self.add('')
+
+ def unique_varassign(self, varname: str) -> Optional[Varassign]:
+ varassigns = self.all_varassigns(varname)
+ return varassigns[0] if len(varassigns) == 1 else None
+
+ def all_varassigns(self, varname: str) -> Sequence[Varassign]:
+ varassigns = []
+ for (i, line) in enumerate(self.lines):
+ m = re.search(r'^(#?[\w+\-]+?)([!+:?]?=)([ \t]*)([^#\\]*?)(\s*)(#.*|)$', line)
+ if m and m[1] == varname:
+ varassigns.append(Varassign(i, m[1], m[2], m[3], m[4], m[5], m[6]))
+ return varassigns
+
+ def set(self, varname: str, new_value: str) -> bool:
+ """ Changes the value of an existing variable in the lines. """
+
+ varassign = self.unique_varassign(varname)
+ if varassign is not None:
+ self.lines[varassign.index] = varassign.varname + varassign.op + varassign.indent + new_value
+ return varassign is not None
+
+ def append(self, varname: str, value: str) -> None:
+ """ Appends to the value of an existing variable in the lines. """
+ if value == '':
+ return
+ varassign = self.unique_varassign(varname)
+ # TODO: add a test for multiple assignments
+ if varassign is not None:
+ before = ' ' if re.search(r'\S$', varassign.value) else ''
+ after = '' if varassign.comment == '' else ' '
+ self.lines[varassign.index] = \
+ varassign.varname + varassign.op + varassign.indent \
+ + varassign.value + before + value + after \
+ + varassign.comment
+ return varassign is not None
+
+ def remove(self, varname: str) -> bool:
+ """ Removes the unique variable assignment. """
+ varassign = self.unique_varassign(varname)
+ if varassign is not None:
+ self.lines.pop(varassign.index)
+ return varassign is not None
+
+ def get(self, varname: str) -> str:
+ """
+ Returns the value from the only variable assignment, or an empty
+ string.
+ """
+ varassign = self.unique_varassign(varname)
+ return varassign.value if varassign is not None else ''
+
+ def remove_if(self, varname: str, expected_value: str) -> bool:
+ """ Removes a variable assignment if its value is the expected one. """
+ for varassign in self.all_varassigns(varname):
+ if varassign.value == expected_value:
+ self.lines.pop(varassign.index)
+ return True
+ return False
+
+ def index(self, pattern: str) -> int:
+ """ Returns the first index where the pattern is found, or -1. """
+ for (i, line) in enumerate(self.lines):
+ if re.search(pattern, line):
+ return i
+ return -1
+
+
+class Adjuster:
+ """
+ The following adjust_* functions are called after the distfiles have
+ been downloaded and extracted. They inspect the extracted files
+ and adjust the variable definitions in the package Makefile.
+ """
+
+ def __init__(self):
+ # the package name, including the version number.
+ self.distname = distname
+
+ # the absolute pathname to the working directory, containing
+ # the extracted distfiles.
+ self.abs_wrkdir = ''
+
+ # the absolute pathname to a subdirectory of abs_wrkdir, typically
+ # containing package-provided Makefiles or configure scripts.
+ self.abs_wrksrc = ''
+
+ # the regular files and directories relative to abs_wrksrc.
+ self.wrksrc_files = []
+ self.wrksrc_dirs = []
+
+ """
+ The following variables may be set by the adjust_* subroutines and
+ will later appear in the package Makefile:
+ """
+
+ # categories for the package, in addition to the usual
+ # parent directory.
+ self.categories: List[str] = []
+
+ # the dependencies of the package, in the form
+ # "package>=version:../../category/package".
+ self.depends = []
+ self.build_depends = []
+ self.test_depends = []
+
+ # .include, interleaved with BUILDLINK3_API_DEPENDS.
+ # These lines are added at the bottom of the Makefile.
+ self.bl3_lines = []
+
+ # a list of pathnames relative to the package path.
+ # All these files will be included at the bottom of the Makefile.
+ self.includes = []
+
+ # a list of variable assignments that will make up the fourth
+ # paragraph of the package Makefile, where the build configuration
+ # takes place.
+ self.build_vars = []
+
+ # similar to the @build_vars, but separated by an empty line in
+ # the Makefile, thereby forming the fifth paragraph.
+ self.extra_vars = []
+
+ # variables from the initial Makefile whose values are replaced
+ self.update_vars: Dict[str, str] = {}
+
+ # these are inserted below the second paragraph in the Makefile.
+ self.todos = []
+
+ # the package name is based on DISTNAME and modified by
+ # pkgname_prefix and pkgname_transform.
+ self.pkgname_prefix = '' # example: ${PYPKGPREFIX}-
+ self.pkgname_transform = '' # example: :S,-v,-,
+
+ # all lines of the package Makefile, for direct modification.
+ self.makefile_lines = Lines()
+
+ self.regenerate_distinfo = False
+
+ def add_dependency(self, kind: str, pkgbase: str, constraint: str, dep_dir: str) -> None:
+ """ add_dependency('DEPENDS', 'package', '>=1', '../../category/package') """
+ if dep_dir != '' and isfile(dep_dir + '/buildlink3.mk'):
+ # TODO: add kind to bl3_lines (BUILDLINK_DEPENDS)
+ # TODO: add constraint to bl3_lines (BUILDLINK_API_DEPENDS)
+ self.bl3_lines.append('.include "%s/buildlink3.mk"' % dep_dir)
+ return
+
+ value = pkgbase + constraint + ':' + dep_dir \
+ if dep_dir != '' and isfile(dep_dir + '/Makefile') \
+ else '# TODO: {0}{1}'.format(pkgbase, constraint)
+
+ if kind == 'DEPENDS':
+ self.depends.append(value)
+ elif kind == 'BUILD_DEPENDS':
+ self.build_depends.append(value)
+ elif kind == 'TEST_DEPENDS':
+ self.test_depends.append(value)
+ else:
+ self.todos.append('dependency {0} {1}'.format(kind, value))
+
+ def read_dependencies(self, cmd: str, env: Dict[str, str], cwd: str, pkgname_prefix: str) -> None:
+ dep_lines = []
+
+ effective_env = dict(os.environ)
+ effective_env.update(env)
+
+ output = subprocess.check_output(
+ args=cmd,
+ shell=True,
+ env=effective_env,
+ cwd=cwd
+ )
+
+ for line in output.decode('utf-8').split('\n'):
+ m = re.search(r'^(\w+)\t([^\s:>]+)(>[^\s:]+|)(?::(\.\./\.\./\S+))?$', line)
+ if m:
+ dep_lines.append([m[1], m[2], m[3] or '>=0', m[4] or ''])
+ continue
+ m = re.search(r'^var\t(\S+)\t(.+)$', line)
+ if m:
+ self.update_vars[m[1]] = m[2]
+ continue
+ if line != '':
+ debug('unknown dependency line: %s', line)
+
+ for dep_line in dep_lines:
+ type, pkgbase, constraint, dir = dep_line
+
+ if dir == '' and pkgname_prefix != '':
+ dir = find_package(pkgname_prefix + pkgbase)
+ if dir != '':
+ pkgbase = pkgname_prefix + pkgbase
+ if dir == '':
+ dir = find_package(pkgbase)
+
+ debug('add_dependency: %s %s %s %s', type, pkgbase, constraint, dir)
+ self.add_dependency(type, pkgbase, constraint, dir)
+
+ def wrksrc_find(self, what: Union[str, Callable]) -> Iterator[str]:
+ def search(f):
+ if type(what) == str:
+ return re.search(what, f)
+ return what(f)
+
+ return list(filter(search, self.wrksrc_files))
+
+ def wrksrc_isdir(self, relative_pathname: str) -> bool:
+ return isfile(self.abs_wrksrc + '/' + relative_pathname)
+
+ def wrksrc_isfile(self, relative_pathname: str) -> bool:
+ return isfile(self.abs_wrksrc + '/' + relative_pathname)
+
+ def wrksrc_open(self, relative_pathname: str):
+ return open(self.abs_wrksrc + '/' + relative_pathname)
+
+ def adjust_configure(self):
+ if not self.wrksrc_isfile('configure'):
+ return
+
+ with self.wrksrc_open('configure') as f:
+ for line in f:
+ if 'autoconf' in line or 'Free Software Foundation' in line:
+ self.build_vars.append(Var('GNU_CONFIGURE', '=', 'yes'))
+ return
+
+ self.build_vars.append(Var('HAS_CONFIGURE', '=', 'yes'))
+
+ def adjust_cmake(self):
+ if self.wrksrc_isfile('CMakeLists.txt'):
+ self.build_vars.append(Var('USE_CMAKE', '=', 'yes'))
+
+ def adjust_meson(self):
+ if self.wrksrc_isfile('meson.build'):
+ self.includes.append('../../devel/py-meson/build.mk')
+
+ def adjust_gconf2_schemas(self):
+ gconf2_files = self.wrksrc_find(r'schemas(?:\.in.*)$')
+ if not gconf2_files:
+ return
+ for f in gconf2_files:
+ m = re.search(r'(.*schemas)', f)
+ if m:
+ self.extra_vars.append(Var('GCONF_SCHEMAS', '+=', m[1]))
+ self.includes.append('../../devel/GConf/schemas.mk')
+
+ def adjust_libtool(self):
+ if self.wrksrc_isfile('ltconfig') or self.wrksrc_isfile('ltmain.sh'):
+ self.build_vars.append(Var('USE_LIBTOOL', '=', 'yes'))
+ if self.wrksrc_isdir('libltdl'):
+ self.includes.append('../../devel/libltdl/convenience.mk')
+
+ def adjust_perl_module_Build_PL(self):
+ """
+ Example packages:
+ devel/p5-Algorithm-CheckDigits
+ """
+ cmd = '%s -I%s -I. Build.PL' % (config.perl5, config.libdir)
+ self.read_dependencies(cmd, {}, self.abs_wrksrc, '')
+ self.build_vars.append(Var('PERL5_MODULE_TYPE', '=', 'Module::Build'))
+
+ def adjust_perl_module_Makefile_PL(self):
+ """
+ Example packages:
+ devel/p5-Algorithm-Diff (no dependencies)
+ devel/p5-Carp-Assert-More (dependencies without version numbers)
+ www/p5-HTML-Quoted (dependency with version number)
+ """
+ # To avoid fix_up_makefile error for p5-HTML-Quoted, generate Makefile first.
+ cmd1 = '%s -I. Makefile.PL < /dev/null 1>&0 2>&0' % config.perl5
+ cmd2 = '%s -I%s -I. Makefile.PL' % (config.perl5, config.libdir)
+ subprocess.call(cmd1, shell=True, cwd=self.abs_wrksrc)
+ self.read_dependencies(cmd2, {}, self.abs_wrksrc, '')
+
+ def adjust_perl_module_homepage(self, url: str) -> None:
+ if '${MASTER_SITE_PERL_CPAN:' in self.makefile_lines.get('MASTER_SITES'):
+ homepage = self.makefile_lines.get('HOMEPAGE')
+ if homepage != '' and url.startswith(homepage):
+ module_name = re.sub(r'-v?[0-9].*', '', self.distname).replace('-', '::')
+ self.makefile_lines.set('HOMEPAGE', f'https://metacpan.org/pod/{module_name}')
+
+ def adjust_perl_module(self, url: str):
+ if self.wrksrc_isfile('Build.PL'):
+ self.adjust_perl_module_Build_PL()
+ elif self.wrksrc_isfile('Makefile.PL'):
+ self.adjust_perl_module_Makefile_PL()
+ else:
+ return
+
+ packlist = re.sub(r'-v?[0-9].*', '', self.distname).replace('-', '/')
+ self.build_vars.append(Var('PERL5_PACKLIST', '=', f'auto/{packlist}/.packlist'))
+ self.includes.append('../../lang/perl5/module.mk')
+ self.pkgname_prefix = 'p5-'
+ self.categories.append('perl5')
+ self.adjust_perl_module_homepage(url)
+
+ os.unlink('PLIST')
+
+ def adjust_python_module(self):
+ """
+ Example packages:
+ devel/py-ZopeComponent (dependencies, test dependencies)
+ devel/py-gflags (uses distutils.core instead of setuptools; BSD license)
+ devel/py-gcovr (uses setuptools; BSD license)
+ """
+
+ if not self.wrksrc_isfile('setup.py'):
+ return
+
+ cmd = '%s setup.py build' % config.pythonbin
+ env = {
+ 'PYTHONDONTWRITEBYTECODE': 'x',
+ 'PYTHONPATH': config.libdir
+ }
+ self.read_dependencies(cmd, env, self.abs_wrksrc, 'py-')
+
+ self.pkgname_prefix = '${PYPKGPREFIX}-'
+ self.categories.append('python')
+ self.includes.append('../../lang/python/egg.mk')
+
+ def adjust_cargo(self):
+ if not self.wrksrc_isfile('Cargo.lock'):
+ return
+
+ with self.wrksrc_open('Cargo.lock') as f:
+ for line in f:
+ # "checksum cargo-package-name cargo-package-version
+ m = re.search(r'^"checksum\s(\S+)\s(\S+)', line)
+ if m:
+ self.build_vars.append(Var('CARGO_CRATE_DEPENDS', '+=', m[1] + '-' + m[2]))
+
+ self.includes.append('../../lang/rust/cargo.mk')
+
+ def adjust_pkg_config(self):
+ def relevant(f: str) -> bool:
+ return f.endswith('.pc.in') and not f.endswith('-uninstalled.pc.in')
+
+ pkg_config_files = self.wrksrc_find(relevant)
+
+ if pkg_config_files:
+ self.build_vars.append(Var('USE_TOOLS', '+=', 'pkg-config'))
+ for f in pkg_config_files:
+ self.extra_vars.append(Var('PKGCONFIG_OVERRIDE', '+=', f))
+
+ def adjust_po(self):
+ if self.wrksrc_find(r'\.g?mo$'):
+ self.build_vars.append(Var('USE_PKGLOCALEDIR', '=', 'yes'))
+
+ def adjust_use_languages(self):
+ languages = []
+
+ if self.wrksrc_find(r'\.(c|xs)$'):
+ languages.append('c')
+ if self.wrksrc_find(r'\.(cpp|c\+\+|cxx|cc|C)$'):
+ languages.append('c++')
+ if self.wrksrc_find(r'\.f$'):
+ languages.append('fortran')
+
+ use_languages = ' '.join(languages)
+ if use_languages == '':
+ use_languages = '# none'
+ if use_languages != 'c':
+ self.build_vars.append(Var('USE_LANGUAGES', '=', use_languages))
+
+ def determine_wrksrc(self):
+ """
+ Sets abs_wrksrc depending on abs_wrkdir and the files found there.
+ """
+
+ def ignore(f: str) -> bool:
+ return f.startswith('.') \
+ or f == 'pax_global_header' \
+ or f == 'package.xml' \
+ or f.endswith('.gemspec')
+
+ files = list(filter(lambda x: not ignore(x), os.listdir(self.abs_wrkdir)))
+
+ if len(files) == 1:
+ if files[0] != self.distname:
+ self.build_vars.append(Var('WRKSRC', '=', '${WRKDIR}/' + files[0]))
+ self.abs_wrksrc = self.abs_wrkdir + '/' + files[0]
+ elif len(files) == 0:
+ self.build_vars.append(Var('WRKSRC', '=', '${WRKDIR}'))
+ self.abs_wrksrc = self.abs_wrkdir
+ else:
+ wrksrc = '${WRKDIR} # More than one possibility -- please check manually.'
+ self.build_vars.append(Var('WRKSRC', '=', wrksrc))
+ self.abs_wrksrc = self.abs_wrkdir
+
+ def adjust_lines_python_module(self, lines: Lines, url: str):
+
+ initial_lines = generate_initial_package_Makefile_lines(url)
+ current_lines = Lines.read_from('Makefile')
+
+ if 'python' not in initial_lines.get('CATEGORIES'):
+ return
+ pkgbase = initial_lines.get('GITHUB_PROJECT')
+ if pkgbase == '':
+ return
+ pkgbase1 = pkgbase[:1]
+ pkgversion_norev = re.sub(r'^v', '', initial_lines.get('DISTNAME'))
+
+ # don't risk to overwrite any changes made by the package developer.
+ if '\n'.join(current_lines.lines) != '\n'.join(initial_lines.lines):
+ lines.lines.insert(-2, '# TODO: Migrate MASTER_SITES to PYPI')
+ return
+
+ tx_lines = Lines(*self.makefile_lines.lines)
+ if (tx_lines.remove('GITHUB_PROJECT')
+ and tx_lines.set('DISTNAME', '%s-%s' % (pkgbase, pkgversion_norev))
+ and tx_lines.set('PKGNAME', '${PYPKGPREFIX}-${DISTNAME}')
+ and tx_lines.set('MASTER_SITES', '${MASTER_SITE_PYPI:=%s/%s/}' % (pkgbase1, pkgbase))
+ and tx_lines.remove('DIST_SUBDIR')
+ and (tx_lines.remove_if('EXTRACT_SUFX', '.zip') or True)):
+ self.makefile_lines = tx_lines
+ self.regenerate_distinfo = True
+
+ def generate_adjusted_Makefile_lines(self, url):
+ marker_index = self.makefile_lines.index(r'^# url2pkg-marker')
+ if marker_index == -1:
+ raise Exception('ERROR: didn\'t find the url2pkg marker in the Makefile.')
+
+ lines = Lines(*self.makefile_lines.lines[: marker_index])
+
+ if lines.index(r'^PKGNAME=') == -1:
+ distname_index = lines.index(r'^DISTNAME=(\t+)')
+ if distname_index != -1:
+ pkgname_line = 'PKGNAME=\t%s${DISTNAME%s}' % (self.pkgname_prefix, self.pkgname_transform)
+ lines.lines.insert(distname_index + 1, pkgname_line)
+
+ if self.todos:
+ for todo in self.todos:
+ lines.add('# TODO: ' + todo)
+ lines.add('')
+
+ depend_vars = []
+ depend_vars.extend(map(lambda d: Var('BUILD_DEPENDS', '+=', d), self.build_depends))
+ depend_vars.extend(map(lambda d: Var('DEPENDS', '+=', d), self.depends))
+ depend_vars.extend(map(lambda d: Var('TEST_DEPENDS', '+=', d), self.test_depends))
+ lines.add_vars(*depend_vars)
+
+ lines.add_vars(*self.build_vars)
+ lines.add_vars(*self.extra_vars)
+
+ lines.add(*self.bl3_lines)
+ lines.add(*map(lambda include: '.include "%s"' % include, self.includes))
+
+ lines.add(*self.makefile_lines.lines[marker_index + 1:])
+
+ lines.append('CATEGORIES', ' '.join(self.categories))
+
+ self.adjust_lines_python_module(lines, url)
+
+ for varname in self.update_vars:
+ lines.set(varname, self.update_vars[varname])
+
+ return lines
+
+ def adjust_package_from_extracted_distfiles(self, url: str):
+
+ debug('Adjusting the Makefile')
+
+ self.abs_wrkdir = show_var('WRKDIR')
+ self.determine_wrksrc()
+ self.wrksrc_files = glob.glob(f'{self.abs_wrksrc}/**', recursive=True)
+ self.wrksrc_dirs = glob.glob(f'{self.abs_wrksrc}/**/', recursive=True)
+
+ self.makefile_lines = Lines.read_from('Makefile')
+
+ self.adjust_configure()
+ self.adjust_cmake()
+ self.adjust_meson()
+ self.adjust_gconf2_schemas()
+ self.adjust_libtool()
+ self.adjust_perl_module(url)
+ self.adjust_python_module()
+ self.adjust_cargo()
+ self.adjust_pkg_config()
+ self.adjust_po()
+ self.adjust_use_languages()
+
+ self.generate_adjusted_Makefile_lines(url).write_to('Makefile')
+
+ if self.regenerate_distinfo:
+ bmake('distinfo')
+
+
+def main():
+ global distname
+
+ if not isfile('../../mk/bsd.pkg.mk'):
+ sys.exit(f'ERROR: {sys.argv[0]} must be run from a package directory (.../pkgsrc/category/package).')
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'v', ['verbose'])
+ for opt in opts:
+ if opt in ('v', 'verbose'):
+ config.verbose = True
+ except getopt.GetoptError:
+ sys.exit(f'usage: {sys.argv[0]} [-v|--verbose] [URL]')
+
+ if len(args) == 0:
+ url = input('URL: ')
+ else:
+ url = args[0]
+
+ extract_cookie = glob.glob('w*/.extract_done')
+ if not extract_cookie or not isfile('Makefile'):
+ generate_initial_package(url)
+ else:
+ distname = show_var('DISTNAME')
+
+ Adjuster().adjust_package_from_extracted_distfiles(url)
+
+ print('')
+ print('Remember to run pkglint when you\'re done.')
+ print('See ../../doc/pkgsrc.txt to get some help.')
+ print('')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/pkgtools/url2pkg/files/url2pkg_test.py b/pkgtools/url2pkg/files/url2pkg_test.py
new file mode 100644
index 00000000000..aef7b538339
--- /dev/null
+++ b/pkgtools/url2pkg/files/url2pkg_test.py
@@ -0,0 +1,447 @@
+# $NetBSD: url2pkg_test.py,v 1.1 2019/10/03 09:37:41 rillig Exp $
+
+import os
+from typing import List
+from url2pkg import *
+
+
+def setup_function(fn):
+ config.pkgsrcdir = os.getenv('PKGSRCDIR')
+ assert config.pkgsrcdir is not None
+ os.chdir(config.pkgsrcdir + '/pkgtools/url2pkg')
+
+
+def vars(vars: List[Var]) -> List[str]:
+ return list(map(lambda var: var.name + var.op + var.value, vars))
+
+
+def test_Lines_add_vars__simple():
+ lines = Lines()
+
+ lines.add_vars(
+ Var("1", "=", "one"),
+ Var("6", "=", "six"),
+ )
+
+ assert lines.lines == [
+ "1=\tone",
+ "6=\tsix",
+ "",
+ ]
+
+
+def test_Lines_add_vars__alignment():
+ lines = Lines()
+
+ lines.add_vars(
+ Var("short", "=", "value"),
+ Var("long_name", "=", "value # comment"),
+ )
+
+ assert lines.lines == [
+ "short=\t\tvalue",
+ "long_name=\tvalue # comment",
+ "",
+ ]
+
+
+def test_Lines_add_vars__operators():
+ lines = Lines()
+
+ lines.add_vars(
+ Var("123456", "+=", "value"),
+ )
+
+ assert lines.lines == [
+ "123456+=\tvalue",
+ "",
+ ]
+
+
+def test_Lines_add_vars__empty():
+ lines = Lines("# initial")
+
+ lines.add_vars()
+
+ # No empty line is added.
+ assert lines.lines == ["# initial"]
+
+
+def test_Lines_append__not_found():
+ lines = Lines()
+
+ lines.append("VARNAME", "value")
+
+ assert lines.lines == []
+
+
+def test_Lines_append__only_comment():
+ lines = Lines("VARNAME=\t\t\t# none")
+
+ lines.append("VARNAME", "value")
+
+ assert lines.lines == ["VARNAME=\t\t\tvalue # none"]
+
+
+def test_Lines_append__value_with_comment():
+ lines = Lines("VARNAME=\tvalue # comment")
+
+ lines.append("VARNAME", "appended")
+
+ assert lines.lines == ["VARNAME=\tvalue appended # comment"]
+
+
+def test_Lines_append__value_without_comment():
+ lines = Lines("VARNAME+=\tvalue")
+
+ assert lines.append("VARNAME", "appended") == True
+
+ assert lines.lines == ["VARNAME+=\tvalue appended"]
+
+
+def test_Lines_set__previously_with_comment():
+ lines = Lines("LICENSE=\t# TODO: see mk/license.mk")
+
+ lines.set("LICENSE", "${PERL5_LICENSE}")
+
+ assert lines.lines == ["LICENSE=\t${PERL5_LICENSE}"]
+
+
+def test_Lines_set__overwrite_comment_with_comment():
+ lines = Lines("#LICENSE=\t# TODO: see mk/license.mk")
+
+ lines.set("#LICENSE", "${PERL5_LICENSE}")
+
+ assert lines.lines == ["#LICENSE=\t${PERL5_LICENSE}"]
+
+
+def test_Lines_set__not_found():
+ lines = Lines("OLD_VAR=\told value # old comment")
+
+ lines.set("NEW_VAR", "new value")
+
+ assert lines.lines == ["OLD_VAR=\told value # old comment"]
+
+
+def test_Lines_index():
+ lines = Lines("1", "2", "345")
+
+ assert lines.index("1") == 0
+ assert lines.index("2") == 1
+ assert lines.index("345") == 2
+ assert lines.index("4") == 2
+
+ assert lines.index(r'^(\d\d)\d$') == 2
+ assert lines.index(r"^\d\s\d$") == -1
+ assert lines.index(r"(\d)") == 0
+
+
+def test_Lines_get():
+ lines = Lines(
+ "VAR=value",
+ "VAR=\tvalue # comment",
+ "UNIQUE=\tunique"
+ )
+
+ assert lines.get("VAR") == "" # too many values
+ assert lines.get("ENOENT") == "" # no value at all
+ assert lines.get("UNIQUE") == "unique"
+
+
+def test_generate_initial_package_Makefile_lines__GitHub_archive():
+ url = "https://github.com/org/proj/archive/v1.0.0.tar.gz"
+
+ lines = generate_initial_package_Makefile_lines(url)
+
+ assert lines.lines == [
+ "# $" + "NetBSD$",
+ "",
+ "GITHUB_PROJECT=\tproj",
+ "DISTNAME=\tv1.0.0",
+ "PKGNAME=\t${GITHUB_PROJECT}-${DISTNAME:S,^v,,}",
+ "CATEGORIES=\tpkgtools",
+ "MASTER_SITES=\t${MASTER_SITE_GITHUB:=org/}",
+ "DIST_SUBDIR=\t${GITHUB_PROJECT}",
+ "",
+ "MAINTAINER=\tINSERT_YOUR_MAIL_ADDRESS_HERE",
+ "HOMEPAGE=\thttps://github.com/org/proj/",
+ "COMMENT=\tTODO: Short description of the package",
+ "#LICENSE=\t# TODO: (see mk/license.mk)",
+ "",
+ "# url2pkg-marker (please do not remove this line.)",
+ ".include \"../../mk/bsd.pkg.mk\""
+ ]
+
+
+def test_generate_initial_package_Makefile_lines__GitHub_release_containing_project_name():
+ url = "https://github.com/org/proj/releases/download/1.0.0/proj.zip"
+
+ lines = generate_initial_package_Makefile_lines(url)
+
+ assert lines.lines == [
+ "# $" + "NetBSD$",
+ "",
+ "GITHUB_PROJECT=\tproj",
+ "DISTNAME=\tproj",
+ "CATEGORIES=\tpkgtools",
+ "MASTER_SITES=\t${MASTER_SITE_GITHUB:=org/}",
+ "GITHUB_RELEASE=\t1.0.0",
+ "EXTRACT_SUFX=\t.zip",
+ "",
+ "MAINTAINER=\tINSERT_YOUR_MAIL_ADDRESS_HERE",
+ "HOMEPAGE=\thttps://github.com/org/proj/",
+ "COMMENT=\tTODO: Short description of the package",
+ "#LICENSE=\t# TODO: (see mk/license.mk)",
+ "",
+ "# url2pkg-marker (please do not remove this line.)",
+ ".include \"../../mk/bsd.pkg.mk\""
+ ]
+
+
+def test_generate_initial_package_Makefile_lines__GitHub_release_not_containing_project_name():
+ url = "https://github.com/org/proj/releases/download/1.0.0/data.zip"
+
+ lines = generate_initial_package_Makefile_lines(url)
+
+ assert lines.lines == [
+ "# $" + "NetBSD$",
+ "",
+ "GITHUB_PROJECT=\tproj",
+ "DISTNAME=\tdata",
+ "CATEGORIES=\tpkgtools",
+ "MASTER_SITES=\t${MASTER_SITE_GITHUB:=org/}",
+ "GITHUB_RELEASE=\t1.0.0",
+ "EXTRACT_SUFX=\t.zip",
+ "DIST_SUBDIR=\t${GITHUB_PROJECT}",
+ "",
+ "MAINTAINER=\tINSERT_YOUR_MAIL_ADDRESS_HERE",
+ "HOMEPAGE=\thttps://github.com/org/proj/",
+ "COMMENT=\tTODO: Short description of the package",
+ "#LICENSE=\t# TODO: (see mk/license.mk)",
+ "",
+ "# url2pkg-marker (please do not remove this line.)",
+ ".include \"../../mk/bsd.pkg.mk\""
+ ]
+
+
+def test_generate_initial_package_Makefile_lines__distname_version_with_v():
+ url = "https://cpan.example.org/Algorithm-CheckDigits-v1.3.2.tar.gz"
+
+ lines = generate_initial_package_Makefile_lines(url)
+
+ assert lines.lines == [
+ "# $" + "NetBSD$",
+ "",
+ "DISTNAME=\tAlgorithm-CheckDigits-v1.3.2",
+ "PKGNAME=\t${DISTNAME:S,-v,-,}",
+ "CATEGORIES=\tpkgtools",
+ "MASTER_SITES=\thttps://cpan.example.org/",
+ "",
+ "MAINTAINER=\tINSERT_YOUR_MAIL_ADDRESS_HERE",
+ "HOMEPAGE=\thttps://cpan.example.org/",
+ "COMMENT=\tTODO: Short description of the package",
+ "#LICENSE=\t# TODO: (see mk/license.mk)",
+ "",
+ "# url2pkg-marker (please do not remove this line.)",
+ ".include \"../../mk/bsd.pkg.mk\""
+ ]
+
+
+def test_Adjuster_read_dependencies():
+ dep_lines = [
+ "DEPENDS\tpackage>=version:../../pkgtools/pkglint",
+ "DEPENDS\tpackage>=version:../../pkgtools/x11-links",
+ "BUILD_DEPENDS\turl2pkg>=1.0",
+ "TEST_DEPENDS\tpkglint",
+ "A line that is not a dependency at all",
+ "",
+ ""
+ ]
+ env = {"URL2PKG_DEPENDENCIES": '\n'.join(dep_lines)}
+ cmd = "printf '%s\n' \"$URL2PKG_DEPENDENCIES\""
+
+ adjuster = Adjuster()
+ adjuster.read_dependencies(cmd, env, '.', '')
+
+ assert os.getenv('URL2PKG_DEPENDENCIES') is None
+
+ assert adjuster.depends == [
+ "package>=version:../../pkgtools/pkglint"
+ ]
+ assert adjuster.bl3_lines == [
+ ".include \"../../pkgtools/x11-links/buildlink3.mk\""
+ ]
+ assert adjuster.build_depends == [
+ "url2pkg>=1.0:../../pkgtools/url2pkg"
+ ]
+ assert adjuster.test_depends == [
+ "pkglint>=0:../../pkgtools/pkglint"
+ ]
+
+
+def test_Adjuster_generate_adjusted_Makefile_lines():
+ adjuster = Adjuster()
+ adjuster.makefile_lines = Lines(
+ "# before 1",
+ "# before 2",
+ "# url2pkg-marker",
+ "# after 1",
+ "# after 2"
+ )
+
+ lines = adjuster.generate_adjusted_Makefile_lines("https://example.org/pkgname-1.0.tar.gz")
+
+ assert lines.lines == [
+ "# before 1",
+ "# before 2",
+ "# after 1",
+ "# after 2"
+ ]
+
+
+def test_Adjuster_generate_adjusted_Makefile_lines__dependencies():
+ adjuster = Adjuster()
+ adjuster.makefile_lines.add(
+ "# $" + "NetBSD$",
+ "",
+ "# url2pkg-marker",
+ ".include \"../../mk/bsd.pkg.mk\""
+ )
+
+ # some dependencies whose directory will not be found
+ adjuster.add_dependency("DEPENDS", "depends", ">=5.0", "../../devel/depends");
+ adjuster.add_dependency("TOOL_DEPENDS", "tool-depends", ">=6.0", "../../devel/tool-depends");
+ adjuster.add_dependency("BUILD_DEPENDS", "build-depends", ">=7.0", "../../devel/build-depends");
+ adjuster.add_dependency("TEST_DEPENDS", "test-depends", ">=8.0", "../../devel/test-depends");
+ # some dependencies whose directory is explicitly given
+ adjuster.depends.append("depends>=11.0:../../devel/depends")
+ adjuster.build_depends.append("build-depends>=12.0:../../devel/build-depends")
+ adjuster.test_depends.append("test-depends>=13.0:../../devel/test-depends")
+
+ lines = adjuster.generate_adjusted_Makefile_lines("https://example.org/pkgname-1.0.tar.gz")
+
+ assert lines.lines == [
+ "# $" + "NetBSD$",
+ "",
+ "# TODO: dependency TOOL_DEPENDS # TODO: tool-depends>=6.0",
+ "",
+ "BUILD_DEPENDS+=\t# TODO: build-depends>=7.0",
+ "BUILD_DEPENDS+=\tbuild-depends>=12.0:../../devel/build-depends",
+ "DEPENDS+=\t# TODO: depends>=5.0",
+ "DEPENDS+=\tdepends>=11.0:../../devel/depends",
+ "TEST_DEPENDS+=\t# TODO: test-depends>=8.0",
+ "TEST_DEPENDS+=\ttest-depends>=13.0:../../devel/test-depends",
+ "",
+ ".include \"../../mk/bsd.pkg.mk\""
+ ]
+
+
+def test_Adjuster_adjust_po__not_found():
+ adjuster = Adjuster()
+
+ adjuster.adjust_po()
+
+ assert adjuster.build_vars == []
+
+
+def test_Adjuster_adjust_po__found():
+ adjuster = Adjuster()
+ adjuster.wrksrc_files = ['share/locale/de.mo']
+
+ adjuster.adjust_po()
+
+ assert vars(adjuster.build_vars) == [
+ 'USE_PKGLOCALEDIR=yes'
+ ]
+
+
+def test_Adjuster_adjust_use_languages__none():
+ adjuster = Adjuster()
+
+ adjuster.adjust_use_languages()
+
+ assert vars(adjuster.build_vars) == [
+ 'USE_LANGUAGES=# none'
+ ]
+
+
+def test_Adjuster_adjust_use_languages__c():
+ adjuster = Adjuster()
+ adjuster.wrksrc_files = ['main.c']
+
+ adjuster.adjust_use_languages()
+
+ assert vars(adjuster.build_vars) == []
+
+
+def test_Adjuster_adjust_use_languages__c_in_subdir():
+ adjuster = Adjuster()
+ adjuster.wrksrc_files = ['subdir/main.c']
+
+ adjuster.adjust_use_languages()
+
+ assert vars(adjuster.build_vars) == []
+
+
+def test_Adjuster_adjust_use_languages__cplusplus_in_subdir():
+ adjuster = Adjuster()
+ adjuster.wrksrc_files = ['subdir/main.cpp']
+
+ adjuster.adjust_use_languages()
+
+ assert vars(adjuster.build_vars) == [
+ 'USE_LANGUAGES=c++'
+ ]
+
+
+def test_Adjuster_adjust_use_languages__cplusplus_and_fortran():
+ adjuster = Adjuster()
+ adjuster.wrksrc_files = ['subdir/main.cpp', 'main.f']
+
+ adjuster.adjust_use_languages()
+
+ assert vars(adjuster.build_vars) == [
+ 'USE_LANGUAGES=c++ fortran'
+ ]
+
+
+def test_Adjuster_adjust_pkg_config__none():
+ adjuster = Adjuster()
+
+ adjuster.adjust_pkg_config()
+
+ assert vars(adjuster.build_vars) == []
+ assert vars(adjuster.extra_vars) == []
+
+
+def test_Adjuster_adjust_pkg_config__pc_in():
+ adjuster = Adjuster()
+ adjuster.wrksrc_files = ['library.pc.in']
+
+ adjuster.adjust_pkg_config()
+
+ assert vars(adjuster.build_vars) == ['USE_TOOLS+=pkg-config']
+ assert vars(adjuster.extra_vars) == ['PKGCONFIG_OVERRIDE+=library.pc.in']
+
+
+def test_Adjuster_adjust_pkg_config__uninstalled_pc_in():
+ adjuster = Adjuster()
+ adjuster.wrksrc_files = ['library-uninstalled.pc.in']
+
+ adjuster.adjust_pkg_config()
+
+ assert vars(adjuster.build_vars) == []
+ assert vars(adjuster.extra_vars) == []
+
+
+def test_Adjuster_adjust_pkg_config__both():
+ adjuster = Adjuster()
+ adjuster.wrksrc_files = [
+ 'library.pc.in',
+ 'library-uninstalled.pc.in'
+ ]
+
+ adjuster.adjust_pkg_config()
+
+ assert vars(adjuster.build_vars) == ['USE_TOOLS+=pkg-config']
+ assert vars(adjuster.extra_vars) == ['PKGCONFIG_OVERRIDE+=library.pc.in']