Implement simple logging; ensure rust2rpm --stdout still works
This commit is contained in:
parent
a090caaa62
commit
67c878e32a
6 changed files with 81 additions and 51 deletions
|
@ -1,4 +1,5 @@
|
||||||
jinja2
|
jinja2
|
||||||
pyparsing
|
pyparsing
|
||||||
requests
|
requests
|
||||||
|
termcolor
|
||||||
tqdm
|
tqdm
|
||||||
|
|
|
@ -18,7 +18,7 @@ import subprocess
|
||||||
import requests
|
import requests
|
||||||
import tqdm
|
import tqdm
|
||||||
|
|
||||||
from . import cfg, licensing, generator, util
|
from . import cfg, licensing, generator, log, util
|
||||||
from .core.metadata import Metadata
|
from .core.metadata import Metadata
|
||||||
|
|
||||||
XDG_CACHE_HOME = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
|
XDG_CACHE_HOME = os.getenv("XDG_CACHE_HOME", os.path.expanduser("~/.cache"))
|
||||||
|
@ -114,11 +114,11 @@ def query_newest_version(crate):
|
||||||
for struct in versions:
|
for struct in versions:
|
||||||
version = struct["num"]
|
version = struct["num"]
|
||||||
if struct["yanked"]:
|
if struct["yanked"]:
|
||||||
print(f'Ignoring yanked version {version}')
|
log.info(f'Ignoring yanked version {version}')
|
||||||
elif re.search('alpha|beta|rc|pre', version):
|
elif re.search('alpha|beta|rc|pre', version):
|
||||||
print(f'Ignoring pre-release version {version}')
|
log.info(f'Ignoring pre-release version {version}')
|
||||||
else:
|
else:
|
||||||
print(f'Found version {version}')
|
log.success(f'Found version {version}')
|
||||||
return version
|
return version
|
||||||
|
|
||||||
raise ValueError("Couldn't find any release versions. Specify a version explicitly.")
|
raise ValueError("Couldn't find any release versions. Specify a version explicitly.")
|
||||||
|
@ -180,29 +180,24 @@ def drop_foreign_dependencies(lines):
|
||||||
|
|
||||||
value = True
|
value = True
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# print(f'{line=}')
|
|
||||||
# [target.'cfg(not(any(target_os="windows", target_os="macos")))'.dependencies]
|
|
||||||
if m := TARGET_DEPENDENCY_LINE.match(line):
|
if m := TARGET_DEPENDENCY_LINE.match(line):
|
||||||
expr = m.group('cfg')
|
expr = m.group('cfg')
|
||||||
expr = ast.literal_eval(expr)
|
expr = ast.literal_eval(expr)
|
||||||
# print(f'matched: {expr=}')
|
|
||||||
try:
|
try:
|
||||||
value = cfg.parse_and_evaluate(expr)
|
value = cfg.parse_and_evaluate(expr)
|
||||||
except (ValueError, cfg.ParseException):
|
except (ValueError, cfg.ParseException):
|
||||||
print(f'Could not evaluate {expr!r}, treating as true')
|
log.warn(f'Could not evaluate {expr!r}, treating as true')
|
||||||
value = True
|
value = True
|
||||||
|
|
||||||
if not value:
|
if not value:
|
||||||
feature = m.group('feature')
|
feature = m.group('feature')
|
||||||
print(f'Skipping section {line.rstrip()} ({feature=})')
|
log.info(f'Skipping section {line.rstrip()} ({feature=})')
|
||||||
dropped_features.add(feature)
|
dropped_features.add(feature)
|
||||||
|
|
||||||
elif line.startswith('['):
|
elif line.startswith('['):
|
||||||
# previous section ended, let's keep printing lines again
|
# previous section ended, let's keep printing lines again
|
||||||
value = True
|
value = True
|
||||||
|
|
||||||
# print(f'→ {value}')
|
|
||||||
|
|
||||||
if value:
|
if value:
|
||||||
good_lines += [line]
|
good_lines += [line]
|
||||||
else:
|
else:
|
||||||
|
@ -349,7 +344,7 @@ def make_diff_metadata(args, crate, version):
|
||||||
diffs = make_patches(args, toml)
|
diffs = make_patches(args, toml)
|
||||||
metadata = Metadata.from_file(toml)
|
metadata = Metadata.from_file(toml)
|
||||||
if len(metadata) > 1:
|
if len(metadata) > 1:
|
||||||
print(f"Warning: multiple metadata for {toml}")
|
log.warn(f"More than one set of metadata found for {toml}, using the first one")
|
||||||
metadata = metadata[0]
|
metadata = metadata[0]
|
||||||
return metadata.name, diffs, metadata, doc_files, license_files
|
return metadata.name, diffs, metadata, doc_files, license_files
|
||||||
else:
|
else:
|
||||||
|
@ -357,12 +352,12 @@ def make_diff_metadata(args, crate, version):
|
||||||
|
|
||||||
with files_from_crate(cratef, crate, version) as (toml, doc_files, license_files):
|
with files_from_crate(cratef, crate, version) as (toml, doc_files, license_files):
|
||||||
if not license_files:
|
if not license_files:
|
||||||
print(f"Warning: no license files detected in {crate}")
|
log.warn(f"No license files detected in {crate}")
|
||||||
|
|
||||||
diffs = make_patches(args, toml)
|
diffs = make_patches(args, toml)
|
||||||
metadata = Metadata.from_file(toml)
|
metadata = Metadata.from_file(toml)
|
||||||
if len(metadata) > 1:
|
if len(metadata) > 1:
|
||||||
print(f"Warning: multiple metadata for {toml}, ignoring everything except the first")
|
log.warn(f"More than one set of metadata found for {toml}, using the first one")
|
||||||
metadata = metadata[0]
|
metadata = metadata[0]
|
||||||
if args.store_crate:
|
if args.store_crate:
|
||||||
shutil.copy2(cratef, os.path.join(os.getcwd(), f"{metadata.name}-{version}.crate"))
|
shutil.copy2(cratef, os.path.join(os.getcwd(), f"{metadata.name}-{version}.crate"))
|
||||||
|
@ -403,30 +398,36 @@ def guess_crate_name():
|
||||||
dist-git directory, hence there'd be a spec file, so we can ignore this.
|
dist-git directory, hence there'd be a spec file, so we can ignore this.
|
||||||
"""
|
"""
|
||||||
specs = glob.glob('*.spec')
|
specs = glob.glob('*.spec')
|
||||||
|
|
||||||
if len(specs) > 1:
|
if len(specs) > 1:
|
||||||
print('Multiple spec files found, cannot guess crate name')
|
log.error(f"Found multiple spec files; unable to determine crate name automatically.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if len(specs) == 1:
|
if len(specs) == 1:
|
||||||
crate = None
|
crate = None
|
||||||
for line in open(specs[0]):
|
spec = specs[0]
|
||||||
|
|
||||||
|
for line in open(spec):
|
||||||
if m := re.match(r'^%(?:global|define)\s+crate\s+(\S+)\s+', line):
|
if m := re.match(r'^%(?:global|define)\s+crate\s+(\S+)\s+', line):
|
||||||
if crate:
|
if crate:
|
||||||
print(f'{specs[0]}: Found duplicated %crate define, cannot guess crate name')
|
log.error(f'Found multiple definitions of the %crate macro in {spec}; ' +
|
||||||
|
f'unable to determine crate name automatically.')
|
||||||
return None
|
return None
|
||||||
crate = m.group(1)
|
crate = m.group(1)
|
||||||
if '%' in crate:
|
if '%' in crate:
|
||||||
print(f'{specs[0]}: Crate name appears to use a macro, and evaluation is not implemented')
|
log.error(f'The value of the %crate macro appears to contain other macros and cannot be parsed.')
|
||||||
return None
|
return None
|
||||||
if crate:
|
if crate:
|
||||||
print(f'{specs[0]}: Found crate name {crate!r}')
|
log.success(f'Found valid spec file {spec!r} for the {crate!r} crate.')
|
||||||
else:
|
else:
|
||||||
print(f'{specs[0]}: %crate define not found, cannot guess crate name')
|
log.error(f'Invalid spec file {spec!r}; unable to determine crate name automatically.')
|
||||||
return crate
|
return crate
|
||||||
|
|
||||||
dirname = os.path.basename(os.getcwd())
|
dirname = os.path.basename(os.getcwd())
|
||||||
if m := re.match('^rust-([a-z+0-9_-]+)$', dirname):
|
if m := re.match('^rust-([a-z+0-9_-]+)$', dirname):
|
||||||
print(f'Using crate name {m.group(1)!r} based on the directory name')
|
crate = m.group(1)
|
||||||
return m.group(1)
|
log.info(f'Using crate name {crate!r} based on the current working directory.')
|
||||||
|
return crate
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -496,7 +497,8 @@ def main():
|
||||||
if args.translate_license:
|
if args.translate_license:
|
||||||
license, comments = licensing.translate_license(args.target, args.crate)
|
license, comments = licensing.translate_license(args.target, args.crate)
|
||||||
if comments:
|
if comments:
|
||||||
print(comments)
|
for comment in comments.split("\n"):
|
||||||
|
log.info(comment)
|
||||||
print(license)
|
print(license)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -518,14 +520,13 @@ def main():
|
||||||
# No specfile, so this is probably a new package
|
# No specfile, so this is probably a new package
|
||||||
if package_info := get_package_info(pkg_name):
|
if package_info := get_package_info(pkg_name):
|
||||||
if args.suffix:
|
if args.suffix:
|
||||||
print(f"Versions {args.suffix}.* of the crate '{metadata.name}' are already")
|
log.warn(f"Version {args.suffix}.* of the crate '{metadata.name}' is already " +
|
||||||
print(f"packaged for Fedora: {package_info['full_url']}")
|
f"packaged for Fedora: {package_info['full_url']}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"Crate '{metadata.name}' is already packaged for Fedora:")
|
log.warn(f"Crate '{metadata.name}' is already packaged for Fedora: " +
|
||||||
print(f"{package_info['full_url']}")
|
f"{package_info['full_url']}")
|
||||||
|
|
||||||
print("Re-run with --no-existence-check if you still want to start from scratch.")
|
log.info("Re-run with --no-existence-check to create a new spec file from scratch.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if args.rpmautospec is None:
|
if args.rpmautospec is None:
|
||||||
|
@ -541,25 +542,25 @@ def main():
|
||||||
|
|
||||||
# clean up configuration files with deprecated names
|
# clean up configuration files with deprecated names
|
||||||
if len(confs) > 1:
|
if len(confs) > 1:
|
||||||
print("WARNING: More than one *rust2rpm.conf file is present in this directory.")
|
log.error("More than one *rust2rpm.conf file is present in this directory. " +
|
||||||
print(" Ensure that there is only one, and that it has the correct contents.")
|
"Ensure that there is only one, and that it has the correct contents.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if ".rust2rpm.conf" in confs and "rust2rpm.conf" not in confs:
|
if ".rust2rpm.conf" in confs and "rust2rpm.conf" not in confs:
|
||||||
os.rename(".rust2rpm.conf", "rust2rpm.conf")
|
os.rename(".rust2rpm.conf", "rust2rpm.conf")
|
||||||
print("Renamed deprecated, hidden .rust2rpm.conf file to rust2rpm.conf.")
|
log.info("• Renamed deprecated, hidden .rust2rpm.conf file to rust2rpm.conf.")
|
||||||
|
|
||||||
if "_rust2rpm.conf" in confs and "rust2rpm.conf" not in confs:
|
if "_rust2rpm.conf" in confs and "rust2rpm.conf" not in confs:
|
||||||
os.rename("_rust2rpm.conf", "rust2rpm.conf")
|
os.rename("_rust2rpm.conf", "rust2rpm.conf")
|
||||||
print("Renamed deprecated _rust2rpm.conf file to rust2rpm.conf.")
|
log.info("• Renamed deprecated _rust2rpm.conf file to rust2rpm.conf.")
|
||||||
|
|
||||||
if args.target not in conf:
|
if args.target not in conf:
|
||||||
conf.add_section(args.target)
|
conf.add_section(args.target)
|
||||||
|
|
||||||
conf_all_features = conf[args.target].getboolean("all-features")
|
conf_all_features = conf[args.target].getboolean("all-features")
|
||||||
if conf_all_features is False and args.all_features:
|
if conf_all_features is False and args.all_features:
|
||||||
print("WARNING: Conflicting settings for enabling all features: The setting is \"false\"")
|
log.warn("Conflicting settings for enabling all features: The setting is \"false\"" +
|
||||||
print(" in rust2rpm.conf but it was enabled with the \"--all-features\" CLI flag.")
|
"in rust2rpm.conf but it was enabled with the \"--all-features\" CLI flag.")
|
||||||
|
|
||||||
spec_contents = generator.spec_file_render(
|
spec_contents = generator.spec_file_render(
|
||||||
args = args,
|
args = args,
|
||||||
|
@ -585,12 +586,12 @@ def main():
|
||||||
else:
|
else:
|
||||||
with open(spec_file, "w") as fobj:
|
with open(spec_file, "w") as fobj:
|
||||||
fobj.write(spec_contents)
|
fobj.write(spec_contents)
|
||||||
print(f'Wrote {fobj.name}')
|
log.success(f'Generated: {fobj.name}.')
|
||||||
for fname, diff in zip(patch_files, diffs):
|
for fname, diff in zip(patch_files, diffs):
|
||||||
if fname:
|
if fname:
|
||||||
with open(fname, "w") as fobj:
|
with open(fname, "w") as fobj:
|
||||||
fobj.writelines(diff)
|
fobj.writelines(diff)
|
||||||
print(f'Wrote {fobj.name}')
|
log.success(f'Generated: {fobj.name}')
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -7,6 +7,8 @@ import sys
|
||||||
import pyparsing as pp
|
import pyparsing as pp
|
||||||
from pyparsing import ParseException
|
from pyparsing import ParseException
|
||||||
|
|
||||||
|
from . import log
|
||||||
|
|
||||||
pp.ParserElement.enablePackrat()
|
pp.ParserElement.enablePackrat()
|
||||||
|
|
||||||
# ConfigurationPredicate :
|
# ConfigurationPredicate :
|
||||||
|
@ -59,7 +61,6 @@ def cfg_grammar():
|
||||||
|
|
||||||
@functools.cache
|
@functools.cache
|
||||||
def evaluate_variable(name):
|
def evaluate_variable(name):
|
||||||
# print(f'evaluate_variable: {expr}')
|
|
||||||
match name:
|
match name:
|
||||||
case 'target_arch':
|
case 'target_arch':
|
||||||
return platform.machine()
|
return platform.machine()
|
||||||
|
@ -91,11 +92,10 @@ def evaluate_variable(name):
|
||||||
return 'unknown'
|
return 'unknown'
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
print(f'Unknown variable {name}, assuming False')
|
log.warn(f'Unknown variable {name}, assuming False')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def evaluate(expr, nested=False):
|
def evaluate(expr, nested=False):
|
||||||
# print(f'evaluate: {expr}')
|
|
||||||
if hasattr(expr, 'asList'):
|
if hasattr(expr, 'asList'):
|
||||||
expr = expr.asList() # compat with pyparsing 2.7.x
|
expr = expr.asList() # compat with pyparsing 2.7.x
|
||||||
match expr:
|
match expr:
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import os as _os
|
import os
|
||||||
import sys as _sys
|
import csv
|
||||||
import csv as _csv
|
import functools
|
||||||
import functools as _functools
|
|
||||||
|
|
||||||
SPDX_TO_FEDORA_CSV = _os.path.dirname(__file__) + '/spdx_to_fedora.csv'
|
from . import log
|
||||||
|
|
||||||
|
SPDX_TO_FEDORA_CSV = os.path.dirname(__file__) + '/spdx_to_fedora.csv'
|
||||||
|
|
||||||
def translate_slashes(license):
|
def translate_slashes(license):
|
||||||
"Replace all slashes with OR, emit warning"
|
"Replace all slashes with OR, emit warning"
|
||||||
split = [l.strip() for l in license.split("/")]
|
split = [l.strip() for l in license.split("/")]
|
||||||
if len(split) > 1:
|
if len(split) > 1:
|
||||||
print('Upstream uses deprecated "/" syntax. Replacing with "OR"',
|
log.info('Upstream uses deprecated "/" syntax. Replacing with "OR"')
|
||||||
file=_sys.stderr)
|
|
||||||
return ' OR '.join(split)
|
return ' OR '.join(split)
|
||||||
|
|
||||||
@_functools.lru_cache()
|
@functools.lru_cache()
|
||||||
def spdx_to_fedora_map():
|
def spdx_to_fedora_map():
|
||||||
with open(SPDX_TO_FEDORA_CSV, newline='') as f:
|
with open(SPDX_TO_FEDORA_CSV, newline='') as f:
|
||||||
reader = _csv.DictReader(f)
|
reader = csv.DictReader(f)
|
||||||
return {line['SPDX License Identifier']: line['Fedora Short Name']
|
return {line['SPDX License Identifier']: line['Fedora Short Name']
|
||||||
for line in reader
|
for line in reader
|
||||||
if line['SPDX License Identifier']}
|
if line['SPDX License Identifier']}
|
||||||
|
@ -52,8 +52,7 @@ def translate_license_fedora(license):
|
||||||
else:
|
else:
|
||||||
final.append(mapped)
|
final.append(mapped)
|
||||||
if mapped != tag:
|
if mapped != tag:
|
||||||
print(f'Upstream license tag {fulltag} translated to {mapped}',
|
log.info(f'Upstream license tag {fulltag} translated to {mapped}')
|
||||||
file=_sys.stderr)
|
|
||||||
return (' '.join(final), '\n'.join(comments) or None)
|
return (' '.join(final), '\n'.join(comments) or None)
|
||||||
|
|
||||||
def translate_license(target, license):
|
def translate_license(target, license):
|
||||||
|
|
28
rust2rpm/log.py
Normal file
28
rust2rpm/log.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
from termcolor import colored
|
||||||
|
|
||||||
|
|
||||||
|
def _eprint(message):
|
||||||
|
print(message, file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
def _wrap(message, prefix):
|
||||||
|
return textwrap.wrap(message, 80, initial_indent=f"{prefix} ", subsequent_indent=" "*(len(prefix) + 1))
|
||||||
|
|
||||||
|
|
||||||
|
def success(message):
|
||||||
|
_eprint(colored("\n".join(_wrap(message, "•")), "green"))
|
||||||
|
|
||||||
|
|
||||||
|
def info(message):
|
||||||
|
_eprint(colored("\n".join(_wrap(message, "•")), "white"))
|
||||||
|
|
||||||
|
|
||||||
|
def warn(message):
|
||||||
|
_eprint(colored("\n".join(_wrap(message, "WARNING")), "yellow"))
|
||||||
|
|
||||||
|
|
||||||
|
def error(message):
|
||||||
|
_eprint(colored("\n".join(_wrap(message, "ERROR")), "red", attrs=["dark"]))
|
|
@ -27,6 +27,7 @@ install_requires =
|
||||||
jinja2
|
jinja2
|
||||||
pyparsing
|
pyparsing
|
||||||
requests
|
requests
|
||||||
|
termcolor
|
||||||
tqdm
|
tqdm
|
||||||
|
|
||||||
[options.package_data]
|
[options.package_data]
|
||||||
|
|
Loading…
Reference in a new issue