mirror of
https://github.com/clearlinux/autospec.git
synced 2026-06-16 02:45:56 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b5d8cf5341 | |||
| cd8d0065aa | |||
| 6aaa12ff76 | |||
| 65cf152900 | |||
| 99a7985f29 | |||
| 9594167cc7 | |||
| c62c42a21d | |||
| a88ffdc2a7 | |||
| 936534a889 | |||
| d73a1e70d8 | |||
| f07e061437 | |||
| d4144f5efd | |||
| 07a959cc83 | |||
| e36a856c50 | |||
| 2618dc3eb1 | |||
| 8b9384758b | |||
| 94c6be068b | |||
| f4a13a5a93 | |||
| 5424026110 | |||
| e822d6e48d | |||
| 9bfe801c96 | |||
| 412ce5ee2e | |||
| 1fa3bdd6e0 | |||
| 4ea76c998e | |||
| b2d28bb55a | |||
| 4d029647d7 | |||
| 5279a11b53 | |||
| a19cdc79b4 | |||
| 185099bba8 | |||
| 7b01f0ba82 | |||
| 6a4b23bb3e | |||
| c6d7bdb41c | |||
| a16ede7440 | |||
| a62a849262 | |||
| eaa4f711da | |||
| f35655a0cc | |||
| 0c573b604b | |||
| 356da62750 | |||
| b5caddc404 | |||
| 2181c1fe68 | |||
| d6606ad5a8 | |||
| 4d708b6fe2 | |||
| 1bec16fc2e | |||
| 9f33e630cd |
@@ -0,0 +1,7 @@
|
||||
## DISCONTINUATION OF PROJECT.
|
||||
|
||||
This project will no longer be maintained by Intel.
|
||||
|
||||
Intel will not provide or guarantee development of or support for this project, including but not limited to, maintenance, bug fixes, new releases or updates. Patches to this project are no longer accepted by Intel. If you have an ongoing need to use this project, are interested in independently developing it, or would like to maintain patches for the community, please create your own fork of the project.
|
||||
|
||||
Contact: webadmin@linux.intel.com
|
||||
+14
@@ -128,6 +128,14 @@ release
|
||||
``.spec``. This is also bumped and generated on existing and new packages,
|
||||
respectively. This results in less manual work via automatic management.
|
||||
|
||||
license_skips
|
||||
Each line in the file should be the path to a license file. That path needs
|
||||
to account for the package tarfile prefix. Likely requires using '*' to be
|
||||
effective (e.g. ``pkgname-*/path/to/license`` where ``*`` handles the version).
|
||||
|
||||
Files paths can contain a single '*' per directory such that
|
||||
a line of ``/foo*/bar*`` is allowed but ``/foo*bar*`` is not.
|
||||
|
||||
$package.license
|
||||
In certain cases, the package license may not be automatically discovered. In
|
||||
this instance, ``autospec`` will exit with an error. Update this file to
|
||||
@@ -204,6 +212,12 @@ ${custom}_provides_ban
|
||||
Controlling the build process
|
||||
------------------------------
|
||||
|
||||
invalid_release_sig
|
||||
This file contains the current version that will **not** have its package
|
||||
file be processed for signature verification (overriding the config_opt).
|
||||
This file will be automatically deleted after a new release and is intended
|
||||
to override a single bad signed release.
|
||||
|
||||
extra_sources
|
||||
This file contains a list of extra files to be added to the ``.spec`` and
|
||||
optionally installed as well. Each non-blank and non-comment line should start
|
||||
|
||||
@@ -155,7 +155,7 @@ def is_file_valid(path):
|
||||
|
||||
def dump_symbols(path):
|
||||
"""Get symbols from a file."""
|
||||
cmd = "nm --defined-only -g --dynamic \"{}\"".format(path)
|
||||
cmd = "nm --defined-only -g --dynamic \"{}\" | c++filt".format(path)
|
||||
lines = None
|
||||
|
||||
ret = set()
|
||||
@@ -163,7 +163,7 @@ def dump_symbols(path):
|
||||
try:
|
||||
lines = get_output(cmd)
|
||||
except Exception as e:
|
||||
print("Fatal error inspecting {}: {}".format(path, e))
|
||||
util.print_fatal("Fatal error inspecting {}: {}".format(path, e))
|
||||
sys.exit(1)
|
||||
for line in lines.split("\n"):
|
||||
line = line.strip()
|
||||
@@ -236,6 +236,7 @@ def examine_abi_host(download_path, results_dir, name):
|
||||
cwd=download_path)
|
||||
except Exception as e:
|
||||
util.print_fatal("Error invoking abireport: {}".format(e))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def examine_abi_fallback(download_path, results_dir, name):
|
||||
@@ -272,6 +273,7 @@ def examine_abi_fallback(download_path, results_dir, name):
|
||||
subprocess.check_call(cmd, shell=True)
|
||||
except Exception as e:
|
||||
util.print_fatal("Error extracting RPMS: {}".format(e))
|
||||
sys.exit(1)
|
||||
|
||||
os.chdir(download_path)
|
||||
collected_files = set()
|
||||
|
||||
+16
-4
@@ -38,7 +38,7 @@ import specfiles
|
||||
import tarball
|
||||
from abireport import examine_abi
|
||||
from logcheck import logcheck
|
||||
from util import binary_in_path, print_fatal, write_out
|
||||
from util import binary_in_path, print_build_failed, print_fatal, write_out
|
||||
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
|
||||
@@ -269,10 +269,17 @@ def package(args, url, name, archives, workingdir):
|
||||
pkg_integrity.check(url, conf, interactive=interactive_mode)
|
||||
pkg_integrity.load_specfile(specfile)
|
||||
|
||||
specfile.write_spec()
|
||||
spec_type = specfile.write_spec()
|
||||
|
||||
while 1:
|
||||
package.package(filemanager, args.mock_config, args.mock_opts, conf, requirements, content, args.cleanup)
|
||||
if spec_type == "template":
|
||||
# specfile template is assumed "correct" and any failures need to be manually addressed
|
||||
break
|
||||
filemanager.load_specfile(specfile)
|
||||
if 'license' in specfile.packages and not conf.config_opts['has_license']:
|
||||
conf.config_opts['has_license'] = True
|
||||
conf.rewrite_config_opts()
|
||||
specfile.write_spec()
|
||||
filemanager.newfiles_printed = 0
|
||||
mock_chroot = "/var/lib/mock/clear-{}/root/builddir/build/BUILDROOT/" \
|
||||
@@ -291,7 +298,11 @@ def package(args, url, name, archives, workingdir):
|
||||
|
||||
if package.success == 0:
|
||||
conf.create_buildreq_cache(content.version, requirements.buildreqs_cache)
|
||||
print_fatal("Build failed, aborting")
|
||||
print_build_failed()
|
||||
sys.exit(1)
|
||||
elif 'license' not in specfile.packages and conf.config_opts['has_license']:
|
||||
print_fatal("package -license subpackage deleted")
|
||||
conf.create_buildreq_cache(content.version, requirements.buildreqs_cache)
|
||||
sys.exit(1)
|
||||
elif os.path.isfile("README.clear"):
|
||||
try:
|
||||
@@ -304,7 +315,8 @@ def package(args, url, name, archives, workingdir):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
check.check_regression(conf.download_path, conf.config_opts['skip_tests'], package.round - 1)
|
||||
if spec_type == "generate":
|
||||
check.check_regression(conf.download_path, conf.config_opts['skip_tests'], package.round - 1)
|
||||
|
||||
examine_abi(conf.download_path, content.name)
|
||||
if os.path.exists("/var/lib/rpm"):
|
||||
|
||||
+12
-9
@@ -22,6 +22,7 @@
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
import util
|
||||
|
||||
@@ -153,7 +154,7 @@ class Build(object):
|
||||
return True
|
||||
self.must_restart = 0
|
||||
self.file_restart = 0
|
||||
is_clean = True
|
||||
fatals = []
|
||||
util.call("sync")
|
||||
with util.open_auto(filename, "r") as rootlog:
|
||||
loglines = rootlog.readlines()
|
||||
@@ -161,9 +162,10 @@ class Build(object):
|
||||
for line in loglines:
|
||||
match = missing_pat.match(line)
|
||||
if match is not None:
|
||||
util.print_fatal("Cannot resolve dependency name: {}".format(match.group(1)))
|
||||
is_clean = False
|
||||
return is_clean
|
||||
fatals.append(f"Cannot resolve dependency name: {match.group(1)}")
|
||||
if fatals:
|
||||
util.print_fatal('\n'.join(fatals))
|
||||
sys.exit(1)
|
||||
|
||||
def parse_build_results(self, filename, returncode, filemanager, config, requirements, content):
|
||||
"""Handle build log contents."""
|
||||
@@ -213,6 +215,8 @@ class Build(object):
|
||||
elif infiles == 1 and "not matching the package arch" not in line:
|
||||
# exclude blank lines from consideration...
|
||||
file = line.strip()
|
||||
if file in ("/usr", "/usr/"):
|
||||
self.must_restart += 1
|
||||
if file and file[0] == "/":
|
||||
filemanager.push_file(file, content.name)
|
||||
|
||||
@@ -283,7 +287,7 @@ class Build(object):
|
||||
mockopts,
|
||||
]
|
||||
|
||||
if not cleanup and self.must_restart == 0 and self.file_restart > 0 and set(filemanager.excludes) == set(filemanager.manual_excludes):
|
||||
if config.config_opts.get('avoid_rebuild') and not cleanup and self.must_restart == 0 and self.file_restart > 0 and set(filemanager.excludes) == set(filemanager.manual_excludes):
|
||||
cmd_args.append("--no-clean")
|
||||
cmd_args.append("--short-circuit=binary")
|
||||
|
||||
@@ -295,12 +299,11 @@ class Build(object):
|
||||
# sanity check the build log
|
||||
if not os.path.exists(config.download_path + "/results/build.log"):
|
||||
util.print_fatal("Mock command failed, results log does not exist. User may not have correct permissions.")
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
if not self.parse_buildroot_log(config.download_path + "/results/root.log", ret):
|
||||
return
|
||||
self.parse_buildroot_log(config.download_path + "/results/root.log", ret)
|
||||
|
||||
self.parse_build_results(config.download_path + "/results/build.log", ret, filemanager, config, requirements, content)
|
||||
if filemanager.has_banned:
|
||||
util.print_fatal("Content in banned paths found, aborting build")
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
+19
-9
@@ -147,7 +147,7 @@ def parse_modules_list(modules_string, is_cmake=False):
|
||||
modules = [m for m in re.split(r'\s*([><]?=|\${?[^}]*}?)\s*', modules_string)]
|
||||
modules = filter(None, modules)
|
||||
else:
|
||||
modules = [m.strip('[]') for m in modules_string.split()]
|
||||
modules = [m.strip('[]').strip('"') for m in modules_string.split()]
|
||||
res = []
|
||||
next_is_ver = False
|
||||
for mod in modules:
|
||||
@@ -508,7 +508,7 @@ class Requirements(object):
|
||||
"""Scan a .cmake or CMakeLists.txt file for what's it's actually looking for."""
|
||||
findpackage = re.compile(r"^[^#]*find_package\((\w+)\b.*\)", re.I)
|
||||
findpackage_multiline = re.compile(r"^[^#]*find_package\((\w+)\b.*", re.I)
|
||||
pkgconfig = re.compile(r"^[^#]*pkg_check_modules\s*\(\w+ (.*)\)", re.I)
|
||||
pkgconfig = re.compile(r"^[^#]*pkg_check_modules\s*\([\w\-]+ (.*)\)", re.I)
|
||||
pkg_search_modifiers = {'REQUIRED', 'QUIET', 'NO_CMAKE_PATH',
|
||||
'NO_CMAKE_ENVIRONMENT_PATH', 'IMPORTED_TARGET'}
|
||||
extractword = re.compile(r'(?:"([^"]+)"|(\S+))(.*)')
|
||||
@@ -518,8 +518,13 @@ class Requirements(object):
|
||||
for line in lines:
|
||||
if match := findpackage.search(line):
|
||||
module = match.group(1)
|
||||
if pkg := cmake_modules.get(module):
|
||||
self.add_buildreq(pkg)
|
||||
if pkgs := cmake_modules.get(module):
|
||||
# Some of the entries in cmake_modules list multiple packages, space-separated, so we need to split.
|
||||
# Otherwise, anything in buildreq_ban would have to match the entire string, not just a single package name.
|
||||
# For example: Png2Ico, extra-cmake-modules png2ico
|
||||
# buildreq_ban would have to contain "extra-cmake-modules png2ico" to match, instead of just "png2ico"
|
||||
for pkg in pkgs.split():
|
||||
self.add_buildreq(pkg)
|
||||
elif findpackage_multiline.search(line):
|
||||
self.findpackage_parse_lines(line, lines, cmake_modules)
|
||||
|
||||
@@ -704,6 +709,7 @@ class Requirements(object):
|
||||
"""Use pypi for getting package requires and metadata."""
|
||||
# First look for a local override
|
||||
pypi_json = ""
|
||||
pypi_name = pypidata.get_pypi_name(name)
|
||||
pypi_file = os.path.join(config.download_path, "pypi.json")
|
||||
if os.path.isfile(pypi_file):
|
||||
with open(pypi_file, "r") as pfile:
|
||||
@@ -711,16 +717,20 @@ class Requirements(object):
|
||||
else:
|
||||
# Try and grab the pypi details for the package
|
||||
if config.alias:
|
||||
name = config.alias
|
||||
pypi_name = pypidata.get_pypi_name(name)
|
||||
pypi_name = config.alias
|
||||
pypi_name = pypidata.get_pypi_name(pypi_name)
|
||||
pypi_json = pypidata.get_pypi_metadata(pypi_name)
|
||||
if pypi_json:
|
||||
if not pypi_json:
|
||||
self.pypi_provides = pypi_name
|
||||
else:
|
||||
try:
|
||||
package_pypi = json.loads(pypi_json)
|
||||
except json.JSONDecodeError:
|
||||
package_pypi = {}
|
||||
package_pypi = {"name": pypi_name}
|
||||
if package_pypi.get("name"):
|
||||
self.pypi_provides = package_pypi["name"]
|
||||
else:
|
||||
self.pypi_provides = pypi_name
|
||||
if package_pypi.get("requires"):
|
||||
for pkg in package_pypi["requires"]:
|
||||
self.add_requires(f"pypi({pkg})", config.os_packages, override=True, subpkg="python3")
|
||||
@@ -758,7 +768,7 @@ class Requirements(object):
|
||||
for dirpath, _, files in os.walk(dirn):
|
||||
default_score = 2 if dirpath == dirn else 1
|
||||
|
||||
if "Cargo.toml" in files:
|
||||
if "Cargo.toml" in files and 'Makefile' not in files:
|
||||
config.set_build_pattern('cargo', default_score)
|
||||
|
||||
if "CMakeLists.txt" in files and "configure.ac" not in files:
|
||||
|
||||
+8
-17
@@ -64,34 +64,32 @@ def scan_for_tests(src_dir, config, requirements, content):
|
||||
if config.config_opts.get('skip_tests') or tests_config:
|
||||
return
|
||||
|
||||
make_command = "ninja" if config.config_opts.get('use_ninja') else "make"
|
||||
makeflags = "%{?_smp_mflags} " if config.parallel_build else ""
|
||||
make_check = "make {}check".format(makeflags)
|
||||
cmake_check = "make test"
|
||||
make_check = "{} {}check".format(make_command, makeflags)
|
||||
cmake_check = "{} test".format(make_command)
|
||||
make_check_openmpi = "module load openmpi\nexport OMPI_MCA_rmaps_base_oversubscribe=1\n" \
|
||||
"make {}check\nmodule unload openmpi".format(makeflags)
|
||||
"{} {}check\nmodule unload openmpi".format(make_command, makeflags)
|
||||
cmake_check_openmpi = "module load openmpi\nexport OMPI_MCA_rmaps_base_oversubscribe=1\n" \
|
||||
"make test\nmodule unload openmpi"
|
||||
"{} test\nmodule unload openmpi".format(make_command)
|
||||
|
||||
if config.config_opts.get('allow_test_failures'):
|
||||
make_check_openmpi = "module load openmpi\nexport OMPI_MCA_rmaps_base_oversubscribe=1\n" \
|
||||
"make {}check || :\nmodule unload openmpi".format(makeflags)
|
||||
"{} {}check || :\nmodule unload openmpi".format(make_command, makeflags)
|
||||
cmake_check_openmpi = "module load openmpi\nexport OMPI_MCA_rmaps_base_oversubscribe=1\n" \
|
||||
"make test || :\nmodule unload openmpi"
|
||||
"{} test || :\nmodule unload openmpi".format(make_command)
|
||||
|
||||
perl_check = "make TEST_VERBOSE=1 test"
|
||||
setup_check = """PYTHONPATH=%{buildroot}$(python -c "import sys; print(sys.path[-1])") python setup.py test"""
|
||||
perl_check = "{} TEST_VERBOSE=1 test".format(make_command)
|
||||
meson_check = "meson test -C builddir --print-errorlogs"
|
||||
if config.config_opts.get('allow_test_failures'):
|
||||
make_check += " || :"
|
||||
cmake_check += " || :"
|
||||
perl_check += " || :"
|
||||
setup_check += " || :"
|
||||
meson_check += " || :"
|
||||
|
||||
testsuites = {
|
||||
"makecheck": make_check,
|
||||
"perlcheck": perl_check,
|
||||
"setup.py": setup_check,
|
||||
"cmake": "cd clr-build; " + cmake_check,
|
||||
"meson": meson_check,
|
||||
}
|
||||
@@ -144,13 +142,6 @@ def scan_for_tests(src_dir, config, requirements, content):
|
||||
elif config.default_pattern in ["cpan"] and "Makefile.PL" in files:
|
||||
tests_config = testsuites["perlcheck"]
|
||||
|
||||
elif config.default_pattern == "distutils3" and "setup.py" in files:
|
||||
with util.open_auto(os.path.join(src_dir, "setup.py"), 'r') as setup_fp:
|
||||
setup_contents = setup_fp.read()
|
||||
|
||||
if "test_suite" in setup_contents or "pbr=True" in setup_contents:
|
||||
tests_config = testsuites["setup.py"]
|
||||
|
||||
elif config.default_pattern == "R":
|
||||
tests_config = "export _R_CHECK_FORCE_SUGGESTS_=false\n" \
|
||||
"R CMD check --no-manual --no-examples --no-codoc . " \
|
||||
|
||||
@@ -49,7 +49,7 @@ def scan_for_changes(download_path, directory, transforms):
|
||||
shutil.copy(source, target)
|
||||
os.chmod(target, 0o644)
|
||||
except Exception as e:
|
||||
print("Error copying file: {}".format(e))
|
||||
util.print_fatal("Error copying file: {}".format(e))
|
||||
sys.exit(1)
|
||||
found.append(item)
|
||||
|
||||
|
||||
+26
-4
@@ -28,7 +28,8 @@ from collections import OrderedDict
|
||||
|
||||
import check
|
||||
import license
|
||||
from util import call, open_auto, print_info, print_warning, write_out
|
||||
from util import (call, open_auto, print_fatal, print_info, print_warning,
|
||||
write_out)
|
||||
|
||||
|
||||
def read_pattern_conf(filename, dest, list_format=False, path=None):
|
||||
@@ -109,6 +110,7 @@ class Config(object):
|
||||
self.custom_summ = ""
|
||||
self.license_fetch = None
|
||||
self.license_show = None
|
||||
self.license_skips = []
|
||||
self.git_uri = None
|
||||
self.os_packages = set()
|
||||
self.config_file = None
|
||||
@@ -156,6 +158,7 @@ class Config(object):
|
||||
}
|
||||
self.config_opts = {}
|
||||
self.config_options = {
|
||||
"avoid_rebuild": "Try to use mock short circuit to avoid full rebuilds",
|
||||
"broken_c++": "extend flags with '-std=gnu++98",
|
||||
"cargo_vendor": "create vendor archive with cargo",
|
||||
"use_lto": "configure build for lto",
|
||||
@@ -190,6 +193,7 @@ class Config(object):
|
||||
"no_glob": "Do not use the replacement pattern for file matching",
|
||||
"allow_exe": "Allow Windows executables (*.exe, *.dll) to be packaged",
|
||||
"use_ninja": "Use ninja build files",
|
||||
"has_license": "Require license subpackage for successful build",
|
||||
}
|
||||
# simple_pattern_pkgconfig patterns
|
||||
# contains patterns for parsing build.log for missing dependencies
|
||||
@@ -297,6 +301,7 @@ class Config(object):
|
||||
(r"Perhaps you should add the directory containing `([a-zA-Z0-9\-:]*)\.pc'", 0, 'pkgconfig'),
|
||||
(r"Program (.*) found: NO", 0, None),
|
||||
(r"Target '[a-zA-Z0-9\-]' can't be generated as '(.*)' could not be found", 0, None),
|
||||
(r"The missing Perl modules are:\s*(\S+)", 0, 'perl'),
|
||||
(r"Unable to `import (.*)`", 0, None),
|
||||
(r"Unable to find '(.*)'", 0, None),
|
||||
(r"Warning: prerequisite ([a-zA-Z:]+) [0-9\.]+ not found.", 0, 'perl'),
|
||||
@@ -317,6 +322,7 @@ class Config(object):
|
||||
(r"checking for [a-zA-Z0-9\_\-]+ in (.*?)\.\.\. no", 0, None),
|
||||
(r"checking for library containing (.*)... no", 0, None),
|
||||
(r"checking for perl module ([a-zA-Z:]+) [0-9\.]+... no", 0, 'perl'),
|
||||
("checking if (.*) is available...", 0, None),
|
||||
(r"configure: error: (?:pkg-config missing|Unable to locate) (.*)", 0, None),
|
||||
(r"configure: error: ([a-zA-Z0-9]+) (?:is required to build|not found)", 0, None),
|
||||
(r"configure: error: Cannot find (.*)\. Make sure", 0, None),
|
||||
@@ -342,7 +348,7 @@ class Config(object):
|
||||
def detect_build_from_url(self, url):
|
||||
"""Detect build patterns and build requirements from the patterns detected in the url."""
|
||||
# R package
|
||||
if "cran.r-project.org" in url or "cran.rstudio.com" in url:
|
||||
if "cran.r-project.org" in url or "cran.rstudio.com" in url or "/pub/cran/" in url:
|
||||
self.set_build_pattern("R", 10)
|
||||
|
||||
# python
|
||||
@@ -487,7 +493,7 @@ class Config(object):
|
||||
config_f = configparser.ConfigParser(interpolation=None)
|
||||
config_f.read(opts_path)
|
||||
if "autospec" not in config_f.sections():
|
||||
print("Missing autospec section in options.conf")
|
||||
print_fatal("Missing autospec section in options.conf")
|
||||
sys.exit(1)
|
||||
|
||||
if 'package' in config_f.sections() and config_f['package'].get('alias'):
|
||||
@@ -709,7 +715,7 @@ class Config(object):
|
||||
config.read(self.config_file)
|
||||
|
||||
if "autospec" not in config.sections():
|
||||
print("Missing autospec section..")
|
||||
print_fatal("Missing autospec section..")
|
||||
sys.exit(1)
|
||||
|
||||
self.git_uri = config['autospec'].get('git', None)
|
||||
@@ -832,6 +838,18 @@ class Config(object):
|
||||
except Exception as e:
|
||||
print_warning(f"Unable to remove buildreq_cache file: {e}")
|
||||
|
||||
invalid_release_sig_file = os.path.join(self.download_path, "invalid_release_sig")
|
||||
content = self.read_conf_file(invalid_release_sig_file)
|
||||
if content and content[0] == version:
|
||||
self.config_opts['verify_required'] = False
|
||||
else:
|
||||
try:
|
||||
os.unlink(invalid_release_sig_file)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except Exception as e:
|
||||
print_warning(f"Unable to remove invalid_release_sig file: {e}")
|
||||
|
||||
content = self.read_conf_file(os.path.join(self.download_path, "pkgconfig_add"))
|
||||
for extra in content:
|
||||
extra = pkgconfig_re.sub(r'\1', extra)
|
||||
@@ -992,6 +1010,10 @@ class Config(object):
|
||||
if not license.add_license(word, self.license_translations, self.license_blacklist):
|
||||
print_warning("{}: blacklisted license {} ignored.".format(self.content.name + ".license", word))
|
||||
|
||||
content = self.read_conf_file(os.path.join(self.download_path, "license_skips"))
|
||||
if content:
|
||||
self.license_skips = self.validate_extras_content(content, "license_skips")
|
||||
|
||||
# cargo_vendors is the output of 'cargo vendor' and should be read as is
|
||||
content = self.read_file(os.path.join(self.download_path, "cargo_vendors"), track=True)
|
||||
if content:
|
||||
|
||||
@@ -71,14 +71,8 @@ def sanitize_counts():
|
||||
"""Validate test counts are within sane bounds."""
|
||||
global total_tests
|
||||
global total_pass
|
||||
global total_fail
|
||||
global total_xfail
|
||||
global total_skip
|
||||
global counted_tests
|
||||
global counted_pass
|
||||
global counted_fail
|
||||
global counted_xfail
|
||||
global counted_skip
|
||||
if total_tests > 0 and total_pass == 0:
|
||||
total_pass = total_tests - total_fail - total_skip - total_xfail
|
||||
|
||||
@@ -170,7 +164,6 @@ def parse_log(log, pkgname=''):
|
||||
global total_fail
|
||||
global total_xfail
|
||||
global total_skip
|
||||
global counted_tests
|
||||
global counted_pass
|
||||
global counted_fail
|
||||
global counted_xfail
|
||||
|
||||
@@ -124,7 +124,9 @@ ACL_TYPE_EXTENDED, acl-dev
|
||||
ALSA, alsa-lib-dev
|
||||
APR, apr-dev
|
||||
APR-util, apr-util-dev
|
||||
ARPACK-NG, arpack-ng-dev
|
||||
ASSIMP, assimp-dev
|
||||
Adwaita, libadwaita-dev
|
||||
Analitza, analitza-dev
|
||||
Analitza5, analitza-dev
|
||||
BABL, babl-dev
|
||||
@@ -175,6 +177,7 @@ EXPAT, expat-dev
|
||||
Eet.h, eet-dev
|
||||
Eigen/Core, eigen-dev
|
||||
Eigen/Dense, eigen-dev
|
||||
Eigen/Sparse, eigen-dev
|
||||
Eigen3, eigen-dev
|
||||
Epoxy, pkgconfig(epoxy)
|
||||
Exiv2, exiv2-dev
|
||||
@@ -284,6 +287,7 @@ GSTREAMER, gstreamer-dev
|
||||
GSettingSchemas, gsettings-desktop-schemas
|
||||
GSettingSchemas, gsettings-desktop-schemas-dev
|
||||
GTK, gtk+-dev
|
||||
GTK4, gtk4-dev
|
||||
GnuTLS, gnutls-dev
|
||||
GooglePerftools, gperftools-dev
|
||||
Gperf, gperf
|
||||
@@ -459,6 +463,7 @@ KF6NewStuff, knewstuff-def
|
||||
KF6Notifications, knotifications-dev
|
||||
KF6NotifyConfig, knotifyconfig-dev
|
||||
KF6Package, kpackage-dev
|
||||
KF6Prison, kprison-dev
|
||||
KF6Purpose, purpose-dev
|
||||
KF6QuickCharts, kquickcharts-dev
|
||||
KF6Runner, krunner-dev
|
||||
@@ -548,6 +553,7 @@ NETTLE, nettle-dev
|
||||
NUMA, numactl-dev
|
||||
Nepomuk, nepomuk-core-dev
|
||||
NetworkManager, NetworkManager-dev
|
||||
Nice, libnice-dev
|
||||
NumPy, pypi-numpy
|
||||
Numa, numactl-dev
|
||||
OPENEXR, openexr-dev
|
||||
@@ -621,6 +627,7 @@ Qt5Xdg, libqtxdg-dev
|
||||
Qt5XdgIconLoader, libqtxdg-dev
|
||||
Qt5XmlPatterns, qtxmlpatterns-dev
|
||||
Qt6Positioning, qt6positioning-dev
|
||||
Qt6Qml, qt6declarative-dev
|
||||
Qt6Quick, qt6declarative-dev
|
||||
Qt6QuickTimeline, qt6quicktimeline-dev
|
||||
Qt6ShaderTools, qt6shadertools-dev
|
||||
@@ -650,6 +657,7 @@ Soprano, soprano-dev
|
||||
SpatialIndex, libspatialindex-dev
|
||||
Sphinx, pypi-sphinx
|
||||
Sqlite, sqlite-autoconf-dev
|
||||
Srtp2, libsrtp-dev
|
||||
Startup notification library, libnotify-dev
|
||||
TBB, tbb-dev
|
||||
TIFF, tiff-dev
|
||||
@@ -1124,6 +1132,7 @@ libsecret-unstable, libsecret-dev
|
||||
libsoup-2.4, libsoup-dev
|
||||
libssh, libssh-dev
|
||||
libssh2.h, libssh2-dev
|
||||
libssl, openssl-dev
|
||||
libsystemd, pkgconfig(systemd)
|
||||
libtirpc (via pkg-config), pkgconfig(libtirpc)
|
||||
libudev.h, systemd-dev
|
||||
@@ -1298,6 +1307,8 @@ pycparser, pypi(pycparser)
|
||||
pycurl, pypi(pycurl)
|
||||
pylint, pypi(pylint)
|
||||
pyperclip, pypi(pyperclip)
|
||||
pypi(openvino), openvino-python3
|
||||
pypi(pytorch), pytorch-python3
|
||||
pytest, pypi-pytest
|
||||
pytest-cov, pypi(pytest_cov)
|
||||
python lxml, pypi(lxml)
|
||||
@@ -1492,6 +1503,7 @@ yaml-cpp, yaml-cpp-dev
|
||||
yarg, pypi-yarg
|
||||
yasm, yasm
|
||||
yytext is a pointer, flex
|
||||
zconf.h, zlib-dev
|
||||
zenity, zenity
|
||||
zip, zip
|
||||
zlib, zlib-dev
|
||||
|
||||
+2
-28
@@ -197,33 +197,6 @@ class FileManager(object):
|
||||
|
||||
return removed
|
||||
|
||||
def globlike_match(self, filename, match_name):
|
||||
"""Compare the filename to the match_name in a way that simulates the shell glob '*'."""
|
||||
fsplit = filename.split('/')
|
||||
if len(fsplit) != len(match_name):
|
||||
return False
|
||||
match = True
|
||||
for fpart, mpart in zip(fsplit, match_name):
|
||||
if fpart != mpart:
|
||||
if '*' not in mpart:
|
||||
match = False
|
||||
break
|
||||
if len(mpart) > len(fpart) + 1:
|
||||
match = False
|
||||
break
|
||||
mpl, mpr = mpart.split('*')
|
||||
try:
|
||||
if fpart.index(mpl) != 0:
|
||||
match = False
|
||||
break
|
||||
if fpart.rindex(mpr) != len(fpart) - len(mpr):
|
||||
match = False
|
||||
break
|
||||
except ValueError:
|
||||
match = False
|
||||
break
|
||||
return match
|
||||
|
||||
def push_file(self, filename, pkg_name):
|
||||
"""Perform a number of checks against the filename and push the filename if appropriate."""
|
||||
if filename in self.files or filename in self.files_blacklist:
|
||||
@@ -245,7 +218,7 @@ class FileManager(object):
|
||||
elif len('/'.join(match_name)) <= (len(norm_filename) + 1):
|
||||
# the match name may be 1 longer due to a glob
|
||||
# being able to match an empty string
|
||||
if self.globlike_match(norm_filename, match_name):
|
||||
if util.globlike_match(norm_filename, match_name):
|
||||
path_prefix = '/' if not match else match.group()
|
||||
self.push_package_file(os.path.join(path_prefix, *match_name), k)
|
||||
return
|
||||
@@ -404,6 +377,7 @@ class FileManager(object):
|
||||
(r"^/lib/systemd/user/", "services"),
|
||||
(r"^/usr/lib/systemd/system/", "services"),
|
||||
(r"^/usr/lib/systemd/user/", "services"),
|
||||
(r"^/usr/lib/udev/hwdb.d", "config"),
|
||||
(r"^/usr/lib/udev/rules.d", "config"),
|
||||
(r"^/usr/lib/modules-load.d", "config"),
|
||||
(r"^/usr/lib/tmpfiles.d", "config"),
|
||||
|
||||
+28
-16
@@ -29,7 +29,7 @@ import urllib.parse
|
||||
|
||||
import chardet
|
||||
import download
|
||||
from util import get_contents, get_sha1sum, print_fatal, print_warning
|
||||
import util
|
||||
|
||||
default_license = "TO BE DETERMINED"
|
||||
|
||||
@@ -55,8 +55,6 @@ def add_license(lic, translations, blacklist):
|
||||
presence in the blacklist. Returns False if no license were added, True
|
||||
otherwise.
|
||||
"""
|
||||
global licenses
|
||||
global license_files
|
||||
lic = lic.strip().strip(',')
|
||||
result = False
|
||||
|
||||
@@ -96,7 +94,7 @@ def decode_license(license):
|
||||
def license_from_copying_hash(copying, srcdir, config, name):
|
||||
"""Add licenses based on the hash of the copying file."""
|
||||
try:
|
||||
data = get_contents(copying)
|
||||
data = util.get_contents(copying)
|
||||
except FileNotFoundError:
|
||||
# LICENSE file is a bad symlink (qemu-4.2.0!)
|
||||
return
|
||||
@@ -109,7 +107,7 @@ def license_from_copying_hash(copying, srcdir, config, name):
|
||||
if not data:
|
||||
return
|
||||
|
||||
hash_sum = get_sha1sum(copying)
|
||||
hash_sum = util.get_sha1sum(copying)
|
||||
|
||||
if config.license_fetch:
|
||||
values = {'hash': hash_sum, 'text': data, 'package': name}
|
||||
@@ -142,9 +140,24 @@ def license_from_copying_hash(copying, srcdir, config, name):
|
||||
else:
|
||||
if not config.license_show:
|
||||
return
|
||||
print_warning("Unknown license {0} with hash {1}".format(copying, hash_sum))
|
||||
util.print_warning("Unknown license {0} with hash {1}".format(copying, hash_sum))
|
||||
hash_url = config.license_show % {'HASH': hash_sum}
|
||||
print_warning("Visit {0} to enter".format(hash_url))
|
||||
util.print_warning("Visit {0} to enter".format(hash_url))
|
||||
|
||||
|
||||
def skip_license(license_path, config):
|
||||
"""Check if a given license file path should be skipped."""
|
||||
skip_name = False
|
||||
for skip in config.license_skips:
|
||||
# handle the common tempfile prefix and normalize for
|
||||
# skip lines without a starting '/'
|
||||
skip = skip if skip[0] != '' else skip[1:]
|
||||
skip_path = ['', 'tmp', '*'] + skip
|
||||
if util.globlike_match(license_path, skip_path):
|
||||
util.print_warning(f"Skip license detected for file at {license_path}")
|
||||
skip_name = True
|
||||
break
|
||||
return skip_name
|
||||
|
||||
|
||||
def scan_for_licenses(srcdir, config, pkg_name):
|
||||
@@ -162,12 +175,13 @@ def scan_for_licenses(srcdir, config, pkg_name):
|
||||
"about_bsd.txt"]
|
||||
# look for files that start with copying or licen[cs]e (but are
|
||||
# not likely scripts) or end with licen[cs]e
|
||||
target_pat = re.compile(r"^((copying)|(licen[cs]e)|(e[dp]l-v\d+))|(licen[cs]e)(\.(txt|xml))?$")
|
||||
target_pat = re.compile(r"^((copying)|(licen[cs]e)|(e[dp]l-v\d+))|(licen[cs]e)(\.(txt|xml))?|(intel simplified software license.*\.txt)$")
|
||||
for dirpath, dirnames, files in os.walk(srcdir):
|
||||
for name in files:
|
||||
if name.lower() in targets or target_pat.search(name.lower()):
|
||||
license_from_copying_hash(os.path.join(dirpath, name),
|
||||
srcdir, config, pkg_name)
|
||||
license_path = os.path.join(dirpath, name)
|
||||
if not skip_license(license_path, config):
|
||||
license_from_copying_hash(license_path, srcdir, config, pkg_name)
|
||||
# Also search for license texts in project trees that are
|
||||
# REUSE-compliant, or are in process of adopting this standard (for
|
||||
# example, KDE ecosystem packages). See https://reuse.software for
|
||||
@@ -179,11 +193,12 @@ def scan_for_licenses(srcdir, config, pkg_name):
|
||||
# named `license` instead.
|
||||
dirbase = os.path.basename(dirpath)
|
||||
if re.search(r'^(LICENSES|licenses?|licensing)$', dirbase) and re.search(r'\.txt$', name):
|
||||
license_from_copying_hash(os.path.join(dirpath, name),
|
||||
srcdir, config, pkg_name)
|
||||
license_path = os.path.join(dirpath, name)
|
||||
if not skip_license(license_path, config):
|
||||
license_from_copying_hash(license_path, srcdir, config, pkg_name)
|
||||
|
||||
if not licenses:
|
||||
print_fatal(" Cannot find any license or a valid {}.license file!\n".format(pkg_name))
|
||||
util.print_fatal(" Cannot find any license or a valid {}.license file!\n".format(pkg_name))
|
||||
sys.exit(1)
|
||||
|
||||
print("Licenses : ", " ".join(sorted(licenses)))
|
||||
@@ -191,9 +206,6 @@ def scan_for_licenses(srcdir, config, pkg_name):
|
||||
|
||||
def load_specfile(specfile):
|
||||
"""Get licenses from the specfile content."""
|
||||
global licenses
|
||||
global license_files
|
||||
global hashes
|
||||
specfile.licenses = licenses if licenses else [default_license]
|
||||
specfile.license_files = sorted(license_files)
|
||||
specfile.hashes = hashes
|
||||
|
||||
+43
-11
@@ -3,12 +3,12 @@
|
||||
# This file is sorted with LC_COLLATE=C
|
||||
# Lines beginning with '#' are ignored.
|
||||
# For strings that start with '#', escape the '#' as '\#'.
|
||||
=========================
|
||||
%
|
||||
%license
|
||||
(LGPL)
|
||||
(new)
|
||||
(specified
|
||||
*
|
||||
+
|
||||
-
|
||||
-MIT
|
||||
@@ -26,6 +26,8 @@
|
||||
3-Clause
|
||||
3-clause
|
||||
3BSD
|
||||
=========================
|
||||
>=-2
|
||||
@CPACK_RPM_PACKAGE_LICENSE@
|
||||
AGPLv3+
|
||||
ALv2
|
||||
@@ -35,7 +37,12 @@ APPLICATION__TYPE
|
||||
ASL
|
||||
Artistic
|
||||
BSD
|
||||
BSD(2
|
||||
BSD(3
|
||||
BSD-2-Clause-Views
|
||||
BSD-3
|
||||
BSD-compatible
|
||||
BSD-derived(Repoze)
|
||||
BSD-like
|
||||
BSD-style
|
||||
BSDL
|
||||
@@ -43,6 +50,7 @@ BSD_3_clause
|
||||
BSDish
|
||||
BSL
|
||||
CC-BY
|
||||
Clause
|
||||
Commercial
|
||||
Corp.
|
||||
Distribution
|
||||
@@ -53,6 +61,7 @@ EPL
|
||||
Eclipse
|
||||
Expat(MIT/X11)
|
||||
Expat/MIT
|
||||
FOUNDATION
|
||||
Foundation
|
||||
FreeBSD
|
||||
GENERAL
|
||||
@@ -60,30 +69,39 @@ GFDL
|
||||
GNU
|
||||
GPL
|
||||
GPL+
|
||||
GPL-1.0-only
|
||||
GPL/BSD
|
||||
GPL/BSD/CPL
|
||||
GPLv2.1
|
||||
General
|
||||
IBM
|
||||
Jupyter
|
||||
LESSER
|
||||
LGPL
|
||||
LGPL+BSD
|
||||
LGPL/MIT
|
||||
LICENCE
|
||||
Lesser
|
||||
Library
|
||||
Licences
|
||||
License
|
||||
License(2.0)
|
||||
License(==-2.0)
|
||||
License(>=-2)
|
||||
License(>=-2.0)
|
||||
License(LGPL)
|
||||
License(MIT)
|
||||
License,
|
||||
License-2
|
||||
License-2.0
|
||||
License-2.0(MPL-2.0)
|
||||
License-3(GPLv3)
|
||||
Licensing
|
||||
Lucent
|
||||
MIT-0
|
||||
MIT/Expat
|
||||
MPL
|
||||
Minpack
|
||||
Modified
|
||||
Mozilla
|
||||
Muddy-MIT
|
||||
@@ -95,9 +113,13 @@ Open
|
||||
PIL
|
||||
PSF-2+
|
||||
PUBLIC
|
||||
PYTHON
|
||||
Permission
|
||||
Public
|
||||
Revised
|
||||
SIL
|
||||
SIP
|
||||
SOFTWARE
|
||||
See
|
||||
Set
|
||||
Software
|
||||
@@ -111,31 +133,46 @@ UNKNOWN
|
||||
Unknown
|
||||
Unkown
|
||||
Unlimited
|
||||
VERSION-2
|
||||
Version
|
||||
Version-2.0
|
||||
Version-3
|
||||
WITH
|
||||
What
|
||||
\#
|
||||
a
|
||||
advertising
|
||||
and
|
||||
any
|
||||
bsd
|
||||
charge
|
||||
classifiers)
|
||||
clause)
|
||||
copy
|
||||
cryptsetup-OpenSSL-exception
|
||||
details
|
||||
domain
|
||||
domain.
|
||||
dual
|
||||
exceptions
|
||||
for
|
||||
free
|
||||
gpl
|
||||
granted
|
||||
hereby
|
||||
http://nmap.org/man/man-legal.html
|
||||
into
|
||||
is
|
||||
it
|
||||
later
|
||||
later(LGPLv2+)
|
||||
license
|
||||
licensing
|
||||
ndg/httpsclient/LICENCE
|
||||
new
|
||||
none
|
||||
obtaining
|
||||
of
|
||||
on
|
||||
open_source
|
||||
option
|
||||
@@ -143,11 +180,16 @@ option)
|
||||
or
|
||||
or(at
|
||||
others
|
||||
person
|
||||
public
|
||||
released
|
||||
software
|
||||
style
|
||||
terms
|
||||
the
|
||||
to
|
||||
under?
|
||||
unencumbered
|
||||
unknown
|
||||
unrestricted
|
||||
uses
|
||||
@@ -160,13 +202,3 @@ with
|
||||
your
|
||||
|
|
||||
~
|
||||
Permission
|
||||
a
|
||||
charge
|
||||
copy
|
||||
granted
|
||||
hereby
|
||||
obtaining
|
||||
of
|
||||
person
|
||||
to
|
||||
|
||||
@@ -16,9 +16,11 @@ Apache License 2.0, Apache-2.0
|
||||
Apache License, Version 2.0, Apache-2.0
|
||||
Apache, Apache-2.0
|
||||
Apache-2, Apache-2.0
|
||||
Apache2, Apache-2.0
|
||||
Apache2.0, Apache-2.0
|
||||
Apachev2, Apache-2.0
|
||||
Artistic-1.0+GPL-1.0, Artistic-1.0 GPL-1.0
|
||||
Artistic/GPL, Artistic-1.0-Perl GPL-1.0-or-later
|
||||
Artistic_2, Artistic-2.0
|
||||
BSD(3-clause), BSD-3-Clause
|
||||
BSD-2-clause, BSD-2-Clause
|
||||
|
||||
@@ -42,7 +42,7 @@ KEYID_TRY = ""
|
||||
KEYID = ""
|
||||
IMPORTED = ""
|
||||
EMAIL = ""
|
||||
GNUPGCONF = """keyserver keys.gnupg.net"""
|
||||
GNUPGCONF = """keyserver keyserver.ubuntu.com"""
|
||||
CMD_TIMEOUT = 20
|
||||
ENV = os.environ
|
||||
INPUT_GETTER_TIMEOUT = 60
|
||||
@@ -96,7 +96,7 @@ class GPGCli(object):
|
||||
util.write_out(os.path.join(_gpghome, 'gpg.conf'), GNUPGCONF)
|
||||
if pubkey is not None:
|
||||
args = self.args + ['--import', pubkey]
|
||||
output, err, code = self.exec_cmd(args)
|
||||
_, err, code = self.exec_cmd(args)
|
||||
if code == -9:
|
||||
raise Exception('Command {} timeout after {} seconds'.format(' '.join(args), CMD_TIMEOUT))
|
||||
elif code != 0:
|
||||
@@ -205,23 +205,22 @@ class Verifier(object):
|
||||
@staticmethod
|
||||
def quit():
|
||||
"""Stop verification."""
|
||||
print('Critical error quitting...')
|
||||
util.print_fatal("Verification required for build (verify_required option set)")
|
||||
print(SEPT)
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
@staticmethod
|
||||
def calc_sum(filepath, digest_algo):
|
||||
"""Use digest_algo to calculate block sum of a file."""
|
||||
BLOCK_SIZE = 4096
|
||||
block_size = 4096
|
||||
with open(filepath, 'rb') as fp:
|
||||
digest = digest_algo()
|
||||
for block in iter(lambda: fp.read(BLOCK_SIZE), b''):
|
||||
for block in iter(lambda: fp.read(block_size), b''):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
def print_result(self, result, err_msg=''):
|
||||
"""Display verification results."""
|
||||
global EMAIL
|
||||
package_name = ''
|
||||
if self.url is not None:
|
||||
package_name = os.path.basename(self.url)
|
||||
@@ -264,12 +263,12 @@ def get_signature_file(package_url, package_path):
|
||||
def compare_keys(newkey, oldkey):
|
||||
"""Key comparison to check against key tampering."""
|
||||
if newkey != oldkey:
|
||||
util.print_error('Public key has changed:\n'
|
||||
util.print_fatal('Public key has changed:\n'
|
||||
' old key: {}\n'
|
||||
' new key: {}\n'
|
||||
'this is a critical security error, quitting...'
|
||||
.format(oldkey, newkey))
|
||||
exit(1)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# sha256sum Verifier
|
||||
@@ -521,7 +520,6 @@ class GPGVerifier(Verifier):
|
||||
|
||||
def quit_verify():
|
||||
"""Halt build due to verification being required."""
|
||||
util.print_error("Verification required for build (verify_required option set)")
|
||||
Verifier.quit()
|
||||
|
||||
|
||||
|
||||
+13
-6
@@ -27,6 +27,15 @@ def pkg_search(name):
|
||||
return False
|
||||
|
||||
|
||||
def fixup_pypi_prefix(name):
|
||||
"""Try and chop off the 'pypi-' or 'python-' prefix for names."""
|
||||
name = name.lower().replace('-', '_')
|
||||
for prefix in ["pypi_", "python_"]:
|
||||
if name.startswith(prefix):
|
||||
name = name[len(prefix):]
|
||||
return name
|
||||
|
||||
|
||||
def get_pypi_name(name, miss=False):
|
||||
"""Try and verify the pypi name for a given package name."""
|
||||
# normalize the name for matching as pypi is case insensitve for search
|
||||
@@ -35,11 +44,9 @@ def get_pypi_name(name, miss=False):
|
||||
if pkg_search(name):
|
||||
return name
|
||||
# Maybe we have a prefix
|
||||
for prefix in ["pypi_", "python_"]:
|
||||
if name.startswith(prefix):
|
||||
name = name[len(prefix):]
|
||||
if pkg_search(name):
|
||||
return name
|
||||
name = fixup_pypi_prefix(name)
|
||||
if pkg_search(name):
|
||||
return name
|
||||
# Some cases where search fails (Sphinx)
|
||||
# Just try the name we were given
|
||||
if miss:
|
||||
@@ -110,7 +117,7 @@ def main():
|
||||
pkg_name = sys.argv[1]
|
||||
pypi_name = get_pypi_name(pkg_name)
|
||||
if not pypi_name:
|
||||
print(f"Couldn't find {pkg_name} in pypi")
|
||||
util.print_fatal(f"Couldn't find {pkg_name} in pypi")
|
||||
sys.exit(1)
|
||||
pypi_metadata = get_pypi_metadata(pypi_name)
|
||||
print(pypi_metadata)
|
||||
|
||||
+120
-24
@@ -26,6 +26,8 @@ import types
|
||||
from collections import OrderedDict
|
||||
|
||||
import git
|
||||
from jinja2 import Environment
|
||||
from jinja2.loaders import DictLoader
|
||||
from util import _file_write, open_auto
|
||||
|
||||
AVX2_CFLAGS = "-march=x86-64-v3"
|
||||
@@ -35,9 +37,9 @@ AVX512_CFLAGS = "-march=x86-64-v4 -mprefer-vector-width=512"
|
||||
AVX512_FCFLAGS = "-march=x86-64-v4 -mprefer-vector-width=256"
|
||||
AVX512_LCFLAGS = "-march=x86-64-v4"
|
||||
AVX512_LFLAGS = "-Wl,-z,x86-64-v4"
|
||||
APX_CFLAGS = "-march=x86-64-v3 -mapxf -mavx10.1"
|
||||
APX_LCFLAGS = "-march=x86-64-v3"
|
||||
APX_LFLAGS = "-Wl,-z,x86-64-v3"
|
||||
APX_CFLAGS = "-march=x86-64-v4 -mapxf"
|
||||
APX_LCFLAGS = "-march=x86-64-v4"
|
||||
APX_LFLAGS = "-Wl,-z,x86-64-v4"
|
||||
|
||||
|
||||
class Specfile(object):
|
||||
@@ -73,12 +75,42 @@ class Specfile(object):
|
||||
|
||||
def write_spec(self):
|
||||
"""Write spec file."""
|
||||
self.specfile = open_auto("{}/{}.spec".format(self.config.download_path, self.name), "w")
|
||||
spec_path = f"{os.path.join(self.config.download_path, self.name)}.spec"
|
||||
self.specfile = open_auto(spec_path, "w")
|
||||
self.specfile.write_strip = types.MethodType(_file_write, self.specfile)
|
||||
|
||||
# last chance to sanitize url for template and build types
|
||||
if self.config.urlban:
|
||||
clean_url = re.sub(self.config.urlban, "localhost", self.url)
|
||||
# Duplicate prefixes entry before we change the url
|
||||
self.content.prefixes[clean_url] = self.content.prefixes.get(self.url)
|
||||
self.url = clean_url
|
||||
|
||||
template_path = f"{spec_path}.template"
|
||||
|
||||
if os.path.isfile(template_path):
|
||||
# make templates have a template build pattern
|
||||
self.config.default_pattern = "template"
|
||||
# spec file comment header
|
||||
self.write_comment_header()
|
||||
|
||||
if os.path.isfile(template_path):
|
||||
with open_auto(template_path) as tfile:
|
||||
template_content = tfile.read()
|
||||
template = Environment(loader=DictLoader({'spec': template_content})).get_template('spec')
|
||||
kw = {
|
||||
'package_name': self.name,
|
||||
'package_version': self.version,
|
||||
'package_url': self.url,
|
||||
'package_release': self.release,
|
||||
}
|
||||
self.specfile.write(template.render(**kw))
|
||||
self.specfile.write_strip('\n')
|
||||
self.specfile.close()
|
||||
# return specfile type built so autospec knows how to
|
||||
# handle build results (template should only builds once)
|
||||
return "template"
|
||||
|
||||
if self.config.config_opts.get('keepstatic'):
|
||||
self._write("%define keepstatic 1\n")
|
||||
|
||||
@@ -110,6 +142,10 @@ class Specfile(object):
|
||||
|
||||
self.specfile.close()
|
||||
|
||||
# return specfile type built so autospec knows how to
|
||||
# handle build results (generate has multiple builds)
|
||||
return "generate"
|
||||
|
||||
def write_comment_header(self):
|
||||
"""Write comment header to spec file."""
|
||||
self._write("#\n")
|
||||
@@ -132,11 +168,6 @@ class Specfile(object):
|
||||
|
||||
def write_nvr(self):
|
||||
"""Write name, version, and release information."""
|
||||
if self.config.urlban:
|
||||
clean_url = re.sub(self.config.urlban, "localhost", self.url)
|
||||
# Duplicate prefixes entry before we change the url
|
||||
self.content.prefixes[clean_url] = self.content.prefixes.get(self.url)
|
||||
self.url = clean_url
|
||||
self._write("Name : {}\n".format(self.name))
|
||||
self._write("Version : {}\n".format(self.version))
|
||||
self._write("Release : {}\n".format(str(self.release)))
|
||||
@@ -295,6 +326,9 @@ class Specfile(object):
|
||||
self._write("Group: Default\n")
|
||||
|
||||
for dep in deps.get(pkg, []):
|
||||
# honor requires_ban for manual overrides
|
||||
if f"{self.name}-{dep}" in self.requirements.banned_requires.get(pkg, []):
|
||||
continue
|
||||
if dep in self.packages:
|
||||
self._write("Requires: {}-{} = %{{version}}-%{{release}}\n".format(self.name, dep))
|
||||
|
||||
@@ -452,12 +486,16 @@ class Specfile(object):
|
||||
self._write_strip(f"gpg --homedir .gnupg --status-fd 1 --verify {self.config.signature_macro} %{{SOURCE0}} > gpg.status")
|
||||
self._write_strip(f"grep -E '^\\[GNUPG:\\] (GOODSIG|EXPKEYSIG) {self.keyid}' gpg.status")
|
||||
self.write_prep_prepend()
|
||||
prefix = self.content.prefixes[self.url]
|
||||
if self.config.default_pattern == 'R':
|
||||
prefix = self.content.tarball_prefix
|
||||
self._write_strip("%setup -q -n " + prefix)
|
||||
else:
|
||||
self._write_strip("%setup -q -n " + prefix)
|
||||
prefix = self.content.prefixes[self.url]
|
||||
if not prefix:
|
||||
prefix = os.path.splitext(os.path.basename(self.url))[0]
|
||||
self._write_strip("%setup -q -c -n " + prefix)
|
||||
else:
|
||||
self._write_strip("%setup -q -n " + prefix)
|
||||
for archive in self.config.sources["archive"]:
|
||||
# Handle various archive types
|
||||
extract_cmd = 'tar xf {}'
|
||||
@@ -502,7 +540,7 @@ class Specfile(object):
|
||||
if not archive_prefix:
|
||||
# Make it up
|
||||
archive_prefix = os.path.splitext(os.path.basename(archive))[0]
|
||||
self._write_strip("cp -r %{{_builddir}}/{0}/* %{{_builddir}}/{1}/{2}"
|
||||
self._write_strip("cp -r %{{_builddir}}/{0}/. %{{_builddir}}/{1}/{2}"
|
||||
.format(archive_prefix,
|
||||
self.content.tarball_prefix,
|
||||
destination))
|
||||
@@ -513,7 +551,7 @@ class Specfile(object):
|
||||
if self.config.subdir:
|
||||
self._write_strip("pushd " + self.config.subdir)
|
||||
self._write_strip("mkdir -p .cargo")
|
||||
self._write_strip(f"echo '{self.config.cargo_vendors}' >> .cargo/config.toml")
|
||||
self._write_strip(f"echo '\n{self.config.cargo_vendors}' >> .cargo/config.toml")
|
||||
if self.config.subdir:
|
||||
self._write_strip("popd")
|
||||
|
||||
@@ -1128,7 +1166,7 @@ class Specfile(object):
|
||||
self.write_build_prepend()
|
||||
self._write_strip("GOAMD64=v3")
|
||||
self._write_strip(f'CFLAGS="$CLEAR_INTERMEDIATE_CFLAGS {APX_CFLAGS} {APX_LFLAGS} "')
|
||||
self._write_strip(f'CXXFLAGS="$CLEAR_INTERMEDIATE_CXXFLAGS {AVX2_CFLAGS} {AVX2_LFLAGS} "')
|
||||
self._write_strip(f'CXXFLAGS="$CLEAR_INTERMEDIATE_CXXFLAGS {APX_CFLAGS} {APX_LFLAGS} "')
|
||||
self._write_strip(f'FFLAGS="$CLEAR_INTERMEDIATE_FFLAGS {APX_CFLAGS} {APX_LFLAGS} "')
|
||||
self._write_strip(f'FCFLAGS="$CLEAR_INTERMEDIATE_FCFLAGS {APX_CFLAGS} "')
|
||||
self._write_strip(f'LDFLAGS="$CLEAR_INTERMEDIATE_LDFLAGS {APX_LCFLAGS} "')
|
||||
@@ -1310,6 +1348,8 @@ class Specfile(object):
|
||||
self.write_lang_c(export_epoch=True)
|
||||
self.write_variables()
|
||||
self.write_profile_payload("autogen")
|
||||
if self.config.subdir:
|
||||
self._write_strip("pushd " + self.config.subdir)
|
||||
self._write_strip("export GOAMD64=v2")
|
||||
self._write_strip("{0}%autogen {1} {2} {3}"
|
||||
.format(self.get_profile_use_flags(),
|
||||
@@ -1318,6 +1358,8 @@ class Specfile(object):
|
||||
self.config.extra_configure64))
|
||||
self.write_make_line()
|
||||
self._write_strip("\n")
|
||||
if self.config.subdir:
|
||||
self._write_strip("popd")
|
||||
if self.config.config_opts['32bit']:
|
||||
self._write_strip("pushd ../build32/" + self.config.subdir)
|
||||
self.write_build_prepend()
|
||||
@@ -1395,6 +1437,9 @@ class Specfile(object):
|
||||
for module in self.config.pypi_overrides:
|
||||
self._write_strip(f"pypi-dep-fix.py . {module}")
|
||||
self._write_strip("python3 -m build --wheel --skip-dependency-check --no-isolation " + self.config.extra_configure)
|
||||
self._write_strip("\n")
|
||||
if self.config.subdir:
|
||||
self._write_strip("popd")
|
||||
|
||||
if self.config.config_opts['use_avx2']:
|
||||
self._write_strip("pushd ../buildavx2/" + self.config.subdir)
|
||||
@@ -1425,8 +1470,6 @@ class Specfile(object):
|
||||
self._write_strip("popd")
|
||||
|
||||
self._write_strip("\n")
|
||||
if self.config.subdir:
|
||||
self._write_strip("popd")
|
||||
self.write_build_append()
|
||||
self.write_check()
|
||||
self._write_strip("%install")
|
||||
@@ -1763,6 +1806,59 @@ class Specfile(object):
|
||||
|
||||
self.write_cmake_install()
|
||||
|
||||
def write_qmake5_pattern(self):
|
||||
"""Write qmake5 build pattern to spec file."""
|
||||
extra_qmake_args = ""
|
||||
if self.config.config_opts['use_clang']:
|
||||
extra_qmake_args = "-spec linux-clang "
|
||||
if self.config.config_opts['use_lto']:
|
||||
extra_qmake_args += "-config ltcg -config fat-static-lto "
|
||||
else:
|
||||
extra_qmake_args += "QMAKE_CFLAGS+=-fno-lto QMAKE_CXXFLAGS+=-fno-lto "
|
||||
|
||||
self.write_prep()
|
||||
self._write_strip("%build")
|
||||
self.write_build_prepend()
|
||||
self.write_proxy_exports()
|
||||
self._write_strip("export LANG=C.UTF-8")
|
||||
self.write_variables()
|
||||
|
||||
if self.config.subdir:
|
||||
self._write_strip("pushd " + self.config.subdir)
|
||||
|
||||
self._write_strip('export QMAKE_CFLAGS="$CFLAGS"')
|
||||
self._write_strip('export QMAKE_CXXFLAGS="$CXXFLAGS"')
|
||||
self._write_strip('export QMAKE_LFLAGS="$LDFLAGS"')
|
||||
self._write_strip('export QMAKE_LIBDIR=/usr/lib64')
|
||||
self._write_strip('export QMAKE_CFLAGS_RELEASE=')
|
||||
self._write_strip('export QMAKE_CXXFLAGS_RELEASE=')
|
||||
|
||||
# Add the qtbase tools to the path
|
||||
self._write_strip('export PATH=/usr/lib64/qt5/bin:$PATH')
|
||||
|
||||
if self.config.make_command:
|
||||
qmake = self.config.make_command
|
||||
else:
|
||||
qmake = "qmake"
|
||||
self._write_strip(f"{qmake} {extra_qmake_args} {self.config.extra_configure}")
|
||||
self._write_strip("test -r config.log && cat config.log")
|
||||
self.write_make_line()
|
||||
|
||||
if self.config.subdir:
|
||||
self._write_strip("popd")
|
||||
|
||||
if self.config.config_opts['use_avx2']:
|
||||
self._write_strip("pushd ../buildavx2/" + self.config.subdir)
|
||||
self._write(f"{qmake} 'QT_CPU_FEATURES.x86_64 += avx avx2 bmi bmi2 f16c fma lzcnt popcnt'\\\n")
|
||||
self._write(f' QMAKE_CFLAGS+="{AVX2_CFLAGS} {AVX2_LFLAGS}" QMAKE_CXXFLAGS+="{AVX2_CFLAGS} {AVX2_LFLAGS}" \\\n')
|
||||
self._write(f' QMAKE_LFLAGS+="{AVX2_LCFLAGS}" {extra_qmake_args} {self.config.extra_configure}\n')
|
||||
self.write_make_line()
|
||||
self._write_strip("popd")
|
||||
|
||||
self.write_build_append()
|
||||
self._write_strip("\n")
|
||||
self.write_make_install()
|
||||
|
||||
def write_qmake_pattern(self):
|
||||
"""Write qmake build pattern to spec file."""
|
||||
extra_qmake_args = ""
|
||||
@@ -1790,6 +1886,9 @@ class Specfile(object):
|
||||
self._write_strip('export QMAKE_CFLAGS_RELEASE=')
|
||||
self._write_strip('export QMAKE_CXXFLAGS_RELEASE=')
|
||||
|
||||
# Add the qt6base tools to the path
|
||||
self._write_strip('export PATH=/usr/lib64/qt6/bin:$PATH')
|
||||
|
||||
if self.config.make_command:
|
||||
qmake = self.config.make_command
|
||||
else:
|
||||
@@ -2024,14 +2123,11 @@ class Specfile(object):
|
||||
self.write_license_files()
|
||||
if self.config.subdir:
|
||||
self._write_strip("pushd " + self.config.subdir)
|
||||
if self.config.install_macro:
|
||||
self._write_strip(self.config.install_macro)
|
||||
else:
|
||||
self._write_strip("cargo install --path .")
|
||||
self._write_strip("mkdir -p %{buildroot}/usr/bin")
|
||||
self._write_strip('pushd "${HOME}/.cargo/bin/"')
|
||||
self._write_strip("mv * %{buildroot}/usr/bin/")
|
||||
self._write_strip("popd")
|
||||
self._write_strip("cargo install --path .")
|
||||
self._write_strip("mkdir -p %{buildroot}/usr/bin")
|
||||
self._write_strip('pushd "${HOME}/.cargo/bin/"')
|
||||
self._write_strip("mv * %{buildroot}/usr/bin/")
|
||||
self._write_strip("popd")
|
||||
if self.config.subdir:
|
||||
self._write_strip("popd")
|
||||
self.write_install_append()
|
||||
|
||||
+10
-6
@@ -120,12 +120,15 @@ class Source():
|
||||
extraction_path = base_path
|
||||
|
||||
extract_method = getattr(self, 'extract_{}'.format(self.type))
|
||||
extract_method(extraction_path)
|
||||
try:
|
||||
extract_method(extraction_path)
|
||||
except tarfile.AbsoluteLinkError:
|
||||
pass
|
||||
|
||||
def extract_tar(self, extraction_path):
|
||||
"""Extract tar in path."""
|
||||
with tarfile.open(self.path) as content:
|
||||
content.extractall(path=extraction_path)
|
||||
content.extractall(path=extraction_path, filter='data')
|
||||
|
||||
def extract_bz2(self, extraction_path):
|
||||
"""Extract plain bz2 file in path."""
|
||||
@@ -144,7 +147,7 @@ class Source():
|
||||
def extract_zst(self, extraction_path):
|
||||
"""Extract zst in path."""
|
||||
with tarfile.open(fileobj=zstd.open(self.path, 'rb'), mode='r|') as content:
|
||||
content.extractall(path=extraction_path)
|
||||
content.extractall(path=extraction_path, filter='data')
|
||||
|
||||
|
||||
def convert_version(ver_str, name):
|
||||
@@ -153,7 +156,7 @@ def convert_version(ver_str, name):
|
||||
# them out with expensive regular expressions
|
||||
banned_subs = ["x86.64", "source", "src", "all", "bin", "release", "rh",
|
||||
"ga", ".ce", "lcms", "onig", "linux", "gc", "sdk", "orig",
|
||||
"jurko", "%2f", "%2F", "%20", "x265"]
|
||||
"jurko", "%2f", "%2F", "%20", "x265", "autotools"]
|
||||
|
||||
# package names may be modified in the version string by adding "lib" for
|
||||
# example. Remove these from the name before trying to remove the name from
|
||||
@@ -169,7 +172,7 @@ def convert_version(ver_str, name):
|
||||
ver_str = ver_str.replace(name.replace(mod, ""), "")
|
||||
|
||||
# replace illegal characters
|
||||
ver_str = ver_str.strip().replace('-', '.').replace('_', '.')
|
||||
ver_str = ver_str.strip().replace('-', '.').replace('_', '.').replace('+', '.')
|
||||
|
||||
# remove banned substrings
|
||||
for sub in banned_subs:
|
||||
@@ -295,7 +298,8 @@ class Content():
|
||||
if "github.com" in self.url:
|
||||
# define regex accepted for valid packages, important for specific
|
||||
# patterns to come before general ones
|
||||
github_patterns = [r"https?://github.com/(.*)/(.*?)/archive/refs/tags/[vVrR]?(.*)\.tar",
|
||||
github_patterns = [r"https?://github.com/(.*)/(.*?)/archive/refs/tags/.*/(.*).tar",
|
||||
r"https?://github.com/(.*)/(.*?)/archive/refs/tags/[vVrR]?(.*)\.tar",
|
||||
r"https?://github.com/(.*)/(.*?)/archive/[v|r]?.*/(.*).tar",
|
||||
r"https?://github.com/(.*)/(.*?)/archive/[-a-zA-Z_]*-(.*).tar",
|
||||
r"https?://github.com/(.*)/(.*?)/archive/[vVrR]?(.*).tar",
|
||||
|
||||
+136
-1
@@ -27,6 +27,101 @@ import sys
|
||||
dictionary_filename = os.path.dirname(__file__) + "/translate.dic"
|
||||
dictionary = [line.strip() for line in open(dictionary_filename, 'r')]
|
||||
os_paths = None
|
||||
ERROR_FILE = 'pumpAutospec'
|
||||
ERROR_ENV = 'AUTOSPEC_UPDATE'
|
||||
|
||||
|
||||
def _log_error(error):
|
||||
write_out(ERROR_FILE, f"{error}\n", mode='a')
|
||||
|
||||
|
||||
def _commit_result():
|
||||
if not os.path.isfile(ERROR_FILE):
|
||||
return
|
||||
call(f"git add {ERROR_FILE}", check=False, stderr=subprocess.DEVNULL)
|
||||
call(f"git commit {ERROR_FILE} -m 'Notes update'", check=False, stderr=subprocess.DEVNULL)
|
||||
call("git push", check=False, stderr=subprocess.DEVNULL)
|
||||
|
||||
|
||||
def _process_line(line, prev_line, current_patch, reported_patches, error):
|
||||
if m := re.match('^Patch #[0-9]+ .(?P<patch>.*).:', line):
|
||||
current_patch[0] = m.group('patch')
|
||||
|
||||
if m := re.match('Hunk #[0-9]+ FAILED at [0-9]+', line):
|
||||
if current_patch[0] not in reported_patches:
|
||||
_log_error("Patch " + current_patch[0] + " does not apply")
|
||||
reported_patches[current_patch[0]] = True
|
||||
return True
|
||||
|
||||
if m := re.match(".*can't find file to patch at input line ", line):
|
||||
if current_patch[0] not in reported_patches:
|
||||
_log_error("Patch " + current_patch[0] + " does not apply")
|
||||
reported_patches[current_patch[0]] = True
|
||||
return True
|
||||
|
||||
if m := re.match('.*meson.build:[0-9]+:[0-9]+: ERROR: Unknown options: "(?P<option>.*)"', line):
|
||||
_log_error("Unknown meson option: " + m.group('option'))
|
||||
return True
|
||||
|
||||
if m := re.match('Error: package ‘(?P<module>.*)’ .* was found, but', line):
|
||||
_log_error("R package " + m.group('module') + " not found")
|
||||
return True
|
||||
|
||||
if m := re.match('.*CMake Error at .*/CMake', prev_line):
|
||||
if m := re.match('(?P<module>.*) not found', line):
|
||||
_log_error("CMake module " + m.group('module') + "not found")
|
||||
return True
|
||||
|
||||
if m := re.match(r'go: download.*connect: connection refused', line):
|
||||
_log_error("Go online update")
|
||||
return True
|
||||
|
||||
if 'Updating crates.io index' in line:
|
||||
_log_error("Rust crates.io online update")
|
||||
return True
|
||||
|
||||
if "error: '__builtin_ctzs' needs isa option -mbmi" in line:
|
||||
_log_error(" error: '__builtin_ctzs' needs isa option -mbmi")
|
||||
return True
|
||||
|
||||
if "error:" in line and 'Bad exit status from' not in line:
|
||||
m = re.match('.*error:(?P<error>.*)', line)
|
||||
if m and not error:
|
||||
_log_error("Compiler: " + m.group('error'))
|
||||
return True
|
||||
|
||||
if m := re.match(r'Could NOT find (?P<package>.*) .missing', line):
|
||||
_log_error("CMake module " + m.group('package') + " not found")
|
||||
return True
|
||||
if m := re.match(r'Could not find a package configuration file provided by (?P<package>.*) with', line):
|
||||
_log_error("CMake module " + m.group('package') + " not found")
|
||||
return True
|
||||
# Unable to find program 'gperf'
|
||||
if m := re.match(r"Failed to find program ‘(?P<module>.*)’", line):
|
||||
_log_error("Failed to find " + m.group('module'))
|
||||
return True
|
||||
if m := re.match(r"Failed to find ‘(?P<module>.*)’", line):
|
||||
_log_error("Failed to find " + m.group('module'))
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _process_build_log(filename):
|
||||
with open_auto(filename, "r") as lfile:
|
||||
lines = lfile.readlines()
|
||||
|
||||
prev_line = ''
|
||||
current_patch = ['']
|
||||
reported_patches = {}
|
||||
error = False
|
||||
for line in lines:
|
||||
if _process_line(line, prev_line, current_patch, reported_patches, error):
|
||||
error = True
|
||||
prev_line = line
|
||||
|
||||
if error:
|
||||
_commit_result()
|
||||
|
||||
|
||||
def call(command, logfile=None, check=True, **kwargs):
|
||||
@@ -61,7 +156,6 @@ def _file_write(self, s):
|
||||
|
||||
def translate(package):
|
||||
"""Convert terms to their alternate definition."""
|
||||
global dictionary
|
||||
for item in dictionary:
|
||||
if item.startswith(package + "="):
|
||||
return item.split("=")[1]
|
||||
@@ -116,9 +210,22 @@ def print_error(message):
|
||||
_print_message(message, 'ERROR', 'red')
|
||||
|
||||
|
||||
def print_build_failed():
|
||||
"""Print final fatal error, color coded for TTYs."""
|
||||
_print_message('Build failed, aborting', 'FATAL', 'red')
|
||||
try:
|
||||
if os.environ.get(ERROR_ENV):
|
||||
_process_build_log('results/build.log')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def print_fatal(message):
|
||||
"""Print fatal error, color coded for TTYs."""
|
||||
_print_message(message, 'FATAL', 'red')
|
||||
if os.environ.get(ERROR_ENV):
|
||||
write_out(ERROR_FILE, f"{message}\n", mode='a')
|
||||
_commit_result()
|
||||
|
||||
|
||||
def print_warning(message):
|
||||
@@ -166,3 +273,31 @@ def open_auto(*args, **kwargs):
|
||||
assert 'encoding' not in kwargs
|
||||
assert 'errors' not in kwargs
|
||||
return open(*args, encoding="utf-8", errors="surrogateescape", **kwargs)
|
||||
|
||||
|
||||
def globlike_match(filename, match_name):
|
||||
"""Compare the filename to the match_name in a way that simulates the shell glob '*'."""
|
||||
fsplit = filename.split('/')
|
||||
if len(fsplit) != len(match_name):
|
||||
return False
|
||||
match = True
|
||||
for fpart, mpart in zip(fsplit, match_name):
|
||||
if fpart != mpart:
|
||||
if '*' not in mpart:
|
||||
match = False
|
||||
break
|
||||
if len(mpart) > len(fpart) + 1:
|
||||
match = False
|
||||
break
|
||||
mpl, mpr = mpart.split('*')
|
||||
try:
|
||||
if fpart.index(mpl) != 0:
|
||||
match = False
|
||||
break
|
||||
if fpart.rindex(mpr) != len(fpart) - len(mpr):
|
||||
match = False
|
||||
break
|
||||
except ValueError:
|
||||
match = False
|
||||
break
|
||||
return match
|
||||
|
||||
@@ -1612,3 +1612,5 @@ http://sourceforge.net/projects/zsh/files/zsh/5.4.2/zsh-5.4.2.tar.gz,zsh,5.4.2
|
||||
https://pigeonhole.dovecot.org/releases/2.3/dovecot-2.3.11-pigeonhole-0.5.11.tar.gz,pigeonhole,0.5.11
|
||||
https://pigeonhole.dovecot.org/releases/2.3/dovecot-2.3-pigeonhole-0.5.20.tar.gz,pigeonhole,0.5.20
|
||||
https://www.ezix.org/software/files/lshw-B.02.19.2.tar.gz,lshw,02.19.2
|
||||
https://github.com/VectorCamp/vectorscan/archive/refs/tags/vectorscan/5.4.11.tar.gz,vectorscan,5.4.11
|
||||
https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.7.13+release.autotools.tar.gz,libopenmpt,0.7.13
|
||||
|
||||
+19
-9
@@ -190,25 +190,36 @@ class TestBuildpattern(unittest.TestCase):
|
||||
def mock_util_call(cmd):
|
||||
del cmd
|
||||
|
||||
def mock_wrap_fatal():
|
||||
output = ['']
|
||||
def mock_print_fatal(msg):
|
||||
output[0] = msg
|
||||
return output, mock_print_fatal
|
||||
|
||||
result, mock_print_fatal = mock_wrap_fatal()
|
||||
|
||||
exit_backup = build.sys.exit
|
||||
call_backup = build.util.call
|
||||
print_backup = build.util.print_fatal
|
||||
build.sys.exit = MagicMock()
|
||||
build.util.call = mock_util_call
|
||||
build.util.print_fatal = mock_print_fatal
|
||||
|
||||
open_name = 'build.util.open_auto'
|
||||
content = "line1\nDEBUG util.py:399: No matching package to install: 'foobar'\nDEBUG util.py:399: No matching package to install: 'foobarbaz'\nline 4"
|
||||
m_open = mock_open(read_data=content)
|
||||
pkg = build.Build()
|
||||
pkg.must_restart = 1
|
||||
pkg.file_restart = 1
|
||||
|
||||
result = True
|
||||
with patch(open_name, m_open, create=True):
|
||||
result = pkg.parse_buildroot_log('testname', 1)
|
||||
pkg.parse_buildroot_log('testname', 1)
|
||||
|
||||
build.util.print_fatal = print_backup
|
||||
build.util.call = call_backup
|
||||
mock_exit = build.sys.exit
|
||||
build.sys.exit = exit_backup
|
||||
|
||||
self.assertFalse(result)
|
||||
self.assertEqual(pkg.must_restart, 0)
|
||||
self.assertEqual(pkg.file_restart, 0)
|
||||
self.assertEqual(result[0], 'Cannot resolve dependency name: foobar\nCannot resolve dependency name: foobarbaz')
|
||||
mock_exit.assert_called_once()
|
||||
|
||||
def test_parse_buildroot_log_pass(self):
|
||||
"""
|
||||
@@ -227,11 +238,10 @@ class TestBuildpattern(unittest.TestCase):
|
||||
|
||||
result = True
|
||||
with patch(open_name, m_open, create=True):
|
||||
result = pkg.parse_buildroot_log('testname', 1)
|
||||
pkg.parse_buildroot_log('testname', 1)
|
||||
|
||||
build.util.call = call_backup
|
||||
|
||||
self.assertTrue(result)
|
||||
self.assertEqual(pkg.must_restart, 0)
|
||||
|
||||
def test_parse_buildroot_log_noop(self):
|
||||
|
||||
@@ -499,6 +499,95 @@ class TestBuildreq(unittest.TestCase):
|
||||
self.assertEqual(self.reqs.requires['python3'], pypi_requires)
|
||||
self.assertEqual(ssummary, summary)
|
||||
|
||||
def test_scan_for_configure_pypi_no_name_in_json(self):
|
||||
"""
|
||||
Test scan_for_configure when distutils is being used for the build
|
||||
pattern to test connecting to pypi but not getting a name back.
|
||||
"""
|
||||
orig_summary = buildreq.specdescription.default_summary
|
||||
orig_sscore = buildreq.specdescription.default_summary_score
|
||||
orig_pypi_name = buildreq.pypidata.get_pypi_name
|
||||
orig_pypi_meta = buildreq.pypidata.get_pypi_metadata
|
||||
name = "pypi-name"
|
||||
content = json.dumps({})
|
||||
buildreq.pypidata.pkg_search = MagicMock(return_value=False)
|
||||
buildreq.pypidata.get_pypi_metadata = MagicMock(return_value=content)
|
||||
with tempfile.TemporaryDirectory() as tmpd:
|
||||
conf = config.Config(tmpd)
|
||||
conf.config_opts['use_ninja'] = False
|
||||
os.mkdir(os.path.join(tmpd, 'subdir'))
|
||||
open(os.path.join(tmpd, 'subdir', 'pyproject.toml'), 'w').close()
|
||||
self.reqs.scan_for_configure(os.path.join(tmpd, 'subdir'), name, conf)
|
||||
|
||||
post_summary = buildreq.specdescription.default_summary
|
||||
buildreq.specdescription.default_summary = orig_summary
|
||||
buildreq.specdescription.default_summary_score = orig_sscore
|
||||
buildreq.pypidata.get_pypi_name = orig_pypi_name
|
||||
buildreq.pypidata.get_pypi_metadata = orig_pypi_meta
|
||||
|
||||
self.assertEqual(self.reqs.pypi_provides, "name")
|
||||
self.assertEqual(post_summary, orig_summary)
|
||||
|
||||
def test_scan_for_configure_pypi_no_json(self):
|
||||
"""
|
||||
Test scan_for_configure when distutils is being used for the build
|
||||
pattern to test being unable to connect to pypi.
|
||||
"""
|
||||
orig_summary = buildreq.specdescription.default_summary
|
||||
orig_sscore = buildreq.specdescription.default_summary_score
|
||||
orig_pypi_name = buildreq.pypidata.get_pypi_name
|
||||
orig_pypi_meta = buildreq.pypidata.get_pypi_metadata
|
||||
name = "pypi-name"
|
||||
buildreq.pypidata.pkg_search = MagicMock(return_value=False)
|
||||
buildreq.pypidata.get_pypi_metadata = MagicMock(return_value="")
|
||||
with tempfile.TemporaryDirectory() as tmpd:
|
||||
conf = config.Config(tmpd)
|
||||
conf.config_opts['use_ninja'] = False
|
||||
os.mkdir(os.path.join(tmpd, 'subdir'))
|
||||
open(os.path.join(tmpd, 'subdir', 'pyproject.toml'), 'w').close()
|
||||
self.reqs.scan_for_configure(os.path.join(tmpd, 'subdir'), name, conf)
|
||||
|
||||
post_summary = buildreq.specdescription.default_summary
|
||||
buildreq.specdescription.default_summary = orig_summary
|
||||
buildreq.specdescription.default_summary_score = orig_sscore
|
||||
buildreq.pypidata.get_pypi_name = orig_pypi_name
|
||||
buildreq.pypidata.get_pypi_metadata = orig_pypi_meta
|
||||
|
||||
self.assertEqual(self.reqs.pypi_provides, "name")
|
||||
self.assertEqual(post_summary, orig_summary)
|
||||
|
||||
def test_scan_for_configure_pypi_bad_json(self):
|
||||
"""
|
||||
Test scan_for_configure when distutils is being used for the build
|
||||
pattern to test being given bad json data.
|
||||
"""
|
||||
orig_summary = buildreq.specdescription.default_summary
|
||||
orig_sscore = buildreq.specdescription.default_summary_score
|
||||
orig_pypi_name = buildreq.pypidata.get_pypi_name
|
||||
orig_pypi_meta = buildreq.pypidata.get_pypi_metadata
|
||||
orig_json_loads = buildreq.json.loads
|
||||
name = "pypi-name"
|
||||
content = json.dumps({})
|
||||
buildreq.pypidata.pkg_search = MagicMock(return_value=False)
|
||||
buildreq.pypidata.get_pypi_metadata = MagicMock(return_value=content)
|
||||
buildreq.json.loads = MagicMock(side_effect=json.JSONDecodeError("", "", 0))
|
||||
with tempfile.TemporaryDirectory() as tmpd:
|
||||
conf = config.Config(tmpd)
|
||||
conf.config_opts['use_ninja'] = False
|
||||
os.mkdir(os.path.join(tmpd, 'subdir'))
|
||||
open(os.path.join(tmpd, 'subdir', 'pyproject.toml'), 'w').close()
|
||||
self.reqs.scan_for_configure(os.path.join(tmpd, 'subdir'), name, conf)
|
||||
|
||||
post_summary = buildreq.specdescription.default_summary
|
||||
buildreq.specdescription.default_summary = orig_summary
|
||||
buildreq.specdescription.default_summary_score = orig_sscore
|
||||
buildreq.pypidata.get_pypi_name = orig_pypi_name
|
||||
buildreq.pypidata.get_pypi_metadata = orig_pypi_meta
|
||||
buildreq.json.loads = orig_json_loads
|
||||
|
||||
self.assertEqual(self.reqs.pypi_provides, "name")
|
||||
self.assertEqual(post_summary, orig_summary)
|
||||
|
||||
def test_scan_for_configure_pypi_override(self):
|
||||
"""
|
||||
Test scan_for_configure when distutils is being used for the build
|
||||
|
||||
@@ -152,26 +152,6 @@ class TestTest(unittest.TestCase):
|
||||
check.os.listdir = listdir_backup
|
||||
self.assertEqual(check.tests_config, 'make TEST_VERBOSE=1 test')
|
||||
|
||||
def test_scan_for_tests_setup(self):
|
||||
"""
|
||||
Test scan_for_tests with setup.py suite
|
||||
"""
|
||||
reqs = buildreq.Requirements("")
|
||||
conf = config.Config("")
|
||||
tcontent = tarball.Content("", "", "", [], conf, "")
|
||||
listdir_backup = os.listdir
|
||||
check.os.listdir = mock_generator(['setup.py'])
|
||||
content = 'test_suite'
|
||||
m_open = mock_open(read_data=content)
|
||||
with patch(self.open_name, m_open, create=True):
|
||||
conf.default_pattern = "distutils3"
|
||||
check.scan_for_tests('pkgdir', conf, reqs, tcontent)
|
||||
|
||||
check.os.listdir = listdir_backup
|
||||
self.assertEqual(check.tests_config,
|
||||
'PYTHONPATH=%{buildroot}$(python -c "import sys; print(sys.path[-1])") '
|
||||
'python setup.py test')
|
||||
|
||||
def test_scan_for_tests_cmake(self):
|
||||
"""
|
||||
Test scan_for_tests with cmake suite
|
||||
|
||||
@@ -6,6 +6,7 @@ import config
|
||||
# Structure: (url, build_pattern)
|
||||
BUILD_PAT_URL = [
|
||||
("https://cran.r-project.org/src/contrib/raster_3.0-12.tar.gz", "R"),
|
||||
("https://ftp.osuosl.org/pub/cran/src/contrib/hexbin_1.28.5.tar.gz", "R"),
|
||||
("http://pypi.debian.net/argparse/argparse-1.4.0.tar.gz", "distutils3"),
|
||||
("https://pypi.python.org/packages/source/T/Tempita/Tempita-0.5.2.tar.gz", "distutils3"),
|
||||
("https://cpan.metacpan.org/authors/id/T/TO/TODDR/IO-Tty-1.14.tar.gz", "cpan"),
|
||||
|
||||
+57
-1
@@ -71,7 +71,7 @@ class TestLicense(unittest.TestCase):
|
||||
conf = config.Config("")
|
||||
conf.setup_patterns()
|
||||
# remove the hash from license_hashes
|
||||
del(conf.license_hashes[license.get_sha1sum('tests/COPYING_TEST')])
|
||||
del(conf.license_hashes[license.util.get_sha1sum('tests/COPYING_TEST')])
|
||||
conf.license_show = "license.show.url"
|
||||
license.license_from_copying_hash('tests/COPYING_TEST', '', conf, '')
|
||||
|
||||
@@ -233,6 +233,62 @@ class TestLicense(unittest.TestCase):
|
||||
self.assertIn("Cannot find any license", out.getvalue())
|
||||
self.assertEqual(license.licenses, [])
|
||||
|
||||
def test_scan_for_licenses_skip(self):
|
||||
"""
|
||||
Test scan_for_licenses in temporary directory with licenses to skip
|
||||
"""
|
||||
conf = config.Config("")
|
||||
conf.setup_patterns()
|
||||
conf.license_skips = [['COPYING']]
|
||||
with open('tests/COPYING_TEST', 'rb') as copyingf:
|
||||
content = copyingf.read()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpd:
|
||||
# create the copying file
|
||||
with open(os.path.join(tmpd, 'COPYING'), 'w') as newcopyingf:
|
||||
newcopyingf.write(content.decode('utf-8'))
|
||||
# create some cruft for testing
|
||||
for testf in ['testlib.c', 'testmain.c', 'testheader.h']:
|
||||
with open(os.path.join(tmpd, testf), 'w') as newtestf:
|
||||
newtestf.write('test content')
|
||||
# let's check that the proper thing is being printed as well
|
||||
out = StringIO()
|
||||
with redirect_stdout(out):
|
||||
with self.assertRaises(SystemExit) as thread:
|
||||
license.scan_for_licenses(tmpd, conf, '')
|
||||
|
||||
self.assertEqual(thread.exception.code, 1)
|
||||
self.assertIn("Cannot find any license", out.getvalue())
|
||||
self.assertEqual(license.licenses, [])
|
||||
|
||||
def test_scan_for_licenses_skip_prefix_slash(self):
|
||||
"""
|
||||
Test scan_for_licenses in temporary directory with licenses to skip
|
||||
"""
|
||||
conf = config.Config("")
|
||||
conf.setup_patterns()
|
||||
conf.license_skips = [['', 'COPYING']]
|
||||
with open('tests/COPYING_TEST', 'rb') as copyingf:
|
||||
content = copyingf.read()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpd:
|
||||
# create the copying file
|
||||
with open(os.path.join(tmpd, 'COPYING'), 'w') as newcopyingf:
|
||||
newcopyingf.write(content.decode('utf-8'))
|
||||
# create some cruft for testing
|
||||
for testf in ['testlib.c', 'testmain.c', 'testheader.h']:
|
||||
with open(os.path.join(tmpd, testf), 'w') as newtestf:
|
||||
newtestf.write('test content')
|
||||
# let's check that the proper thing is being printed as well
|
||||
out = StringIO()
|
||||
with redirect_stdout(out):
|
||||
with self.assertRaises(SystemExit) as thread:
|
||||
license.scan_for_licenses(tmpd, conf, '')
|
||||
|
||||
self.assertEqual(thread.exception.code, 1)
|
||||
self.assertIn("Cannot find any license", out.getvalue())
|
||||
self.assertEqual(license.licenses, [])
|
||||
|
||||
def test_load_specfile(self):
|
||||
"""
|
||||
Test load_specfile with populated license list. This method is not
|
||||
|
||||
+1
-14
@@ -47,7 +47,7 @@ class TestSpecfileWrite(unittest.TestCase):
|
||||
self.WRITES = self.WRITES[:4] + self.WRITES[6:]
|
||||
self.assertEqual(expect, self.WRITES)
|
||||
|
||||
def test_write_nvr_no_urlban(self):
|
||||
def test_write_nvr(self):
|
||||
"""
|
||||
test Specfile.write_nvr with no urlban set
|
||||
"""
|
||||
@@ -59,19 +59,6 @@ class TestSpecfileWrite(unittest.TestCase):
|
||||
"Source0 : http://www.testpkg.com/testpkg/pkg-1.0.tar.gz\n"]
|
||||
self.assertEqual(expect, self.WRITES)
|
||||
|
||||
def test_write_nvr_urlban(self):
|
||||
"""
|
||||
test Specfile.write_nvr with urlban set
|
||||
"""
|
||||
self.specfile.config.urlban = "www.testpkg.com"
|
||||
self.specfile.write_nvr()
|
||||
expect = ["Name : pkg\n",
|
||||
"Version : 1.0\n",
|
||||
"Release : 2\n",
|
||||
"URL : http://localhost/testpkg/pkg-1.0.tar.gz\n",
|
||||
"Source0 : http://localhost/testpkg/pkg-1.0.tar.gz\n"]
|
||||
self.assertEqual(expect, self.WRITES)
|
||||
|
||||
def test_write_sources(self):
|
||||
"""
|
||||
test write_sources with all Specfile.sources set.
|
||||
|
||||
+68
-1
@@ -2,7 +2,8 @@ import subprocess
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
import unittest.mock
|
||||
from unittest.mock import MagicMock, mock_open, patch
|
||||
|
||||
import util
|
||||
|
||||
|
||||
@@ -64,5 +65,71 @@ class TestUtil(unittest.TestCase):
|
||||
self.assertTrue(util.binary_in_path('testbin'))
|
||||
self.assertEqual(util.os_paths, [tmpd])
|
||||
|
||||
def test__process_build_log_bad_patch(self):
|
||||
"""
|
||||
Test _process_build_log with a bad patch
|
||||
"""
|
||||
def isfile_mock(_):
|
||||
return True
|
||||
isfile_backup = util.os.path.isfile
|
||||
util.os.path.isfile = isfile_mock
|
||||
call_backup = util.call
|
||||
util.call = MagicMock()
|
||||
open_name = 'util.open_auto'
|
||||
content = "Patch #1 (bad.patch):\nHunk #1 FAILED at 1."
|
||||
m_open = mock_open(read_data=content)
|
||||
with patch(open_name, m_open, create=True):
|
||||
util._process_build_log('filename')
|
||||
|
||||
util.os.path.isfile = isfile_backup
|
||||
mock_call = util.call
|
||||
util.call = call_backup
|
||||
self.assertTrue(len(mock_call.mock_calls) == 3)
|
||||
|
||||
def test_globlike_match(self):
|
||||
"""
|
||||
Test globlike_match
|
||||
"""
|
||||
match_name = ['a', 'b', 'c']
|
||||
file_path = 'a/b'
|
||||
self.assertFalse(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', 'c']
|
||||
file_path = 'a/b'
|
||||
self.assertFalse(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', 'bb*']
|
||||
file_path = 'a/b'
|
||||
self.assertFalse(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', 'b*']
|
||||
file_path = 'a/ab'
|
||||
self.assertFalse(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', '*a']
|
||||
file_path = 'a/ab'
|
||||
self.assertFalse(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', 'c*']
|
||||
file_path = 'a/b'
|
||||
self.assertFalse(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', '*c']
|
||||
file_path = 'a/b'
|
||||
self.assertFalse(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', 'b*']
|
||||
file_path = 'a/b'
|
||||
self.assertTrue(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', '*b']
|
||||
file_path = 'a/b'
|
||||
self.assertTrue(util.globlike_match(file_path, match_name))
|
||||
|
||||
match_name = ['a', 'b']
|
||||
file_path = 'a/b'
|
||||
self.assertTrue(util.globlike_match(file_path, match_name))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(buffer=True)
|
||||
|
||||
Reference in New Issue
Block a user