From 53ad3db66e1727bd26e44e282d1f5f72c6867798 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 21 Nov 2021 21:48:53 +0530 Subject: [PATCH] Command to switch to Qt6 branch and build deps --- setup/commands.py | 8 +- setup/port.py | 245 ++-------------------------------------------- 2 files changed, 11 insertions(+), 242 deletions(-) diff --git a/setup/commands.py b/setup/commands.py index c8d56a604b..8e2d121b13 100644 --- a/setup/commands.py +++ b/setup/commands.py @@ -13,7 +13,7 @@ __all__ = [ 'git_version', 'develop', 'install', 'kakasi', 'rapydscript', 'cacerts', 'recent_uas', 'resources', - 'check', 'to3', 'unicode_check', 'iterators_check', 'test', 'test_rs', + 'check', 'to6', 'test', 'test_rs', 'sdist', 'bootstrap', 'extdev', 'manual', 'tag_release', 'upload_to_server', @@ -68,10 +68,8 @@ gui = GUI() from setup.check import Check check = Check() -from setup.port import To3, UnicodeCheck, IteratorsCheck -to3 = To3() -unicode_check = UnicodeCheck() -iterators_check = IteratorsCheck() +from setup.port import To6 +to6 = To6() from setup.test import Test, TestRS test = Test() diff --git a/setup/port.py b/setup/port.py index 80f408cbd0..7449f7b6ea 100644 --- a/setup/port.py +++ b/setup/port.py @@ -3,248 +3,19 @@ # License: GPL v3 Copyright: 2019, Kovid Goyal -import errno -import hashlib -import json -import os -import re import subprocess import sys -from contextlib import contextmanager -from setup import Command, build_cache_dir, dump_json +from setup import Command -@contextmanager -def modified_file(path, modify): - with open(path, 'r+b') as f: - raw = f.read() - nraw = modify(raw) - modified = nraw != raw - if modified: - f.seek(0), f.truncate(), f.write(nraw), f.flush() - f.seek(0) - try: - yield - finally: - if modified: - f.seek(0), f.truncate(), f.write(raw) +class To6(Command): - -def no2to3(raw): - return re.sub(br'^.+?\s+# no2to3$', b'', raw, flags=re.M) - - -def run_2to3(path, show_diffs=False): - from lib2to3.main import main - with modified_file(path, no2to3): - cmd = [ - '-f', 'all', - '-f', 'buffer', - '-f', 'idioms', - '-f', 'set_literal', - '-x', 'future', - path, - ] - if not show_diffs: - cmd.append('--no-diffs') - - ret = main('lib2to3.fixes', cmd + [path]) - return ret - - -class Base(Command): - - scan_all_files = False - EXCLUDED_BASENAMES = {'Zeroconf.py', 'smtplib.py'} - - @property - def cache_file(self): - return self.j(build_cache_dir(), self.CACHE) - - def is_cache_valid(self, f, cache): - return cache.get(f) == self.file_hash(f) - - def save_cache(self, cache): - dump_json(cache, self.cache_file) - - def get_files(self): - from calibre import walk - for path in walk(os.path.join(self.SRC, 'calibre')): - if (path.endswith('.py') and not path.endswith('_ui.py') and not - os.path.basename(path) in self.EXCLUDED_BASENAMES): - yield path - - def file_hash(self, f): - try: - return self.fhash_cache[f] - except KeyError: - self.fhash_cache[f] = ans = hashlib.sha1(open(f, 'rb').read()).hexdigest() - return ans + description = 'Toggle between the Qt6 and master branches building everything needed' def run(self, opts): - self.fhash_cache = {} - cache = {} - try: - cache = json.load(open(self.cache_file, 'rb')) - except EnvironmentError as err: - if err.errno != errno.ENOENT: - raise - dirty_files = tuple(f for f in self.get_files() if not self.is_cache_valid(f, cache)) - try: - if self.scan_all_files: - bad_files = [] - for f in dirty_files: - if self.file_has_errors(f): - bad_files.append(f) - else: - cache[f] = self.file_hash(f) - dirty_files = bad_files - for i, f in enumerate(dirty_files): - num_left = len(dirty_files) - i - 1 - self.info('\tChecking', f) - if self.file_has_errors(f): - self.report_file_error(f, num_left) - self.fhash_cache.pop(f, None) - cache[f] = self.file_hash(f) - finally: - self.save_cache(cache) - - def clean(self): - try: - os.remove(self.cache_file) - except EnvironmentError as err: - if err.errno != errno.ENOENT: - raise - - -class To3(Base): - - description = 'Run 2to3 and fix anything it reports' - CACHE = 'check2to3.json' - - def report_file_error(self, f, num_left): - run_2to3(f, show_diffs=True) - self.info('%d files left to check' % num_left) - raise SystemExit(1) - - def file_has_errors(self, f): - from polyglot.io import PolyglotStringIO - oo, oe = sys.stdout, sys.stderr - sys.stdout = sys.stderr = buf = PolyglotStringIO() - try: - ret = run_2to3(f) - finally: - sys.stdout, sys.stderr = oo, oe - if ret: - raise SystemExit('Could not parse: ' + f) - output = buf.getvalue() - return re.search(r'^RefactoringTool: No changes to ' + f, output, flags=re.M) is None - - -def edit_file(f): - subprocess.Popen([ - 'vim', '-S', os.path.join(Command.SRC, '../session.vim'), '-f', f - ]).wait() - - -class UnicodeCheck(Base): - - description = 'Check for unicode porting status' - CACHE = 'check_unicode.json' - scan_all_files = True - - def get_error_statement(self, f): - uni_pat = re.compile(r'from __future__ import .*\bunicode_literals\b') - str_pat = re.compile(r'\bstr\(') - has_unicode_literals = False - has_str_calls = False - num_lines = 0 - for i, line in enumerate(open(f, 'rb')): - line = line.decode('utf-8') - if not line.strip(): - continue - num_lines += 1 - if not has_unicode_literals and uni_pat.match(line) is not None: - has_unicode_literals = True - if not has_str_calls and str_pat.search(line) is not None: - has_str_calls = True - if has_unicode_literals and has_str_calls: - break - if num_lines < 1: - return - ans = None - if not has_unicode_literals: - if has_str_calls: - ans = 'The file %s does not use unicode literals and has str() calls' - else: - ans = 'The file %s does not use unicode literals' - elif has_str_calls: - ans = 'The file %s has str() calls' - return ans % f if ans else None - - def file_has_errors(self, f): - return self.get_error_statement(f) is not None - - def report_file_error(self, f, num_left): - edit_file(f) - self.info('%d files left to check' % num_left) - if self.file_has_errors(f): - raise SystemExit(self.get_error_statement(f)) - - -def has_import(text, module, name): - pat = re.compile(r'^from\s+{}\s+import\s+.*\b{}\b'.format(module, name), re.MULTILINE) - if pat.search(text) is not None: - return True - pat = re.compile(r'^from\s+{}\s+import\s+\([^)]*\b{}\b'.format(module, name), re.MULTILINE | re.DOTALL) - if pat.search(text) is not None: - return True - return False - - -class IteratorsCheck(Base): - - description = 'Check for builtins changed to return iterators porting status' - CACHE = 'check_iterators.json' - - def get_errors_in_file(self, f): - pat = re.compile(r'\b(range|map|filter|zip)\(') - with open(f, 'rb') as f: - text = f.read().decode('utf-8') - matches = tuple(pat.finditer(text)) - if not matches: - return [] - ans = [] - names = {m.group(1) for m in matches} - imported_names = {n for n in names if has_import(text, 'polyglot.builtins', n)} - safe_funcs = 'list|tuple|set|frozenset|join' - func_pat = r'({})\('.format(safe_funcs) - for_pat = re.compile(r'\bfor\s+.+?\s+\bin\b') - for i, line in enumerate(text.splitlines()): - m = pat.search(line) - if m is not None: - itname = m.group(1) - if itname in imported_names: - continue - start = m.start() - if start > 0: - if line[start-1] == '*': - continue - if line[start-1] == '(': - if re.search(func_pat + itname, line) is not None: - continue - fm = for_pat.search(line) - if fm is not None and fm.start() < start: - continue - ans.append('%s:%s' % (i, itname)) - return ans - - def file_has_errors(self, f): - return bool(self.get_errors_in_file(f)) - - def report_file_error(self, f, num_left): - edit_file(f) - self.info('%d files left to check' % num_left) - if self.file_has_errors(f): - raise SystemExit('\n'.join(self.get_errors_in_file(f))) + subprocess.check_call(['git', 'switch', '-']) + subprocess.check_call([sys.executable, 'setup.py', 'build', '--clean']) + subprocess.check_call([sys.executable, 'setup.py', 'build']) + subprocess.check_call([sys.executable, 'setup.py', 'gui', '--clean']) + subprocess.check_call([sys.executable, 'setup.py', 'gui'])