First version
This commit is contained in:
commit
243e92a9dd
84 files changed
+4012
No files matched your search
Executable
+129
@@ -0,0 +1,129 @@
|
||||
#!/usr/bin/python3
|
||||
"""Integration checks in the built image; graphical checks are recorded separately."""
|
||||
import configparser
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
passed = failed = 0
|
||||
|
||||
def check(name, fn):
|
||||
global passed, failed
|
||||
try:
|
||||
fn()
|
||||
print('PASS ', name, flush=True)
|
||||
passed += 1
|
||||
except Exception as error:
|
||||
print('FAIL ', name, ':', error, flush=True)
|
||||
failed += 1
|
||||
|
||||
def require(condition, detail='assertion failed'):
|
||||
if not condition:
|
||||
raise AssertionError(detail)
|
||||
|
||||
def run(args, **kwargs):
|
||||
result = subprocess.run(args, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=45, **kwargs)
|
||||
require(result.returncode == 0, result.stdout)
|
||||
return result.stdout
|
||||
|
||||
samples = Path(__file__).parent / 'ide_samples'
|
||||
with tempfile.TemporaryDirectory(prefix='ACPC IDE check ') as directory:
|
||||
work = Path(directory)
|
||||
for filename in ('sum.c', 'sum.cpp', 'Sum.java', 'sum.py'):
|
||||
case = work / filename.replace('.', '-')
|
||||
case.mkdir()
|
||||
source = case / filename
|
||||
shutil.copy(samples / filename, source)
|
||||
def exercise(source=source):
|
||||
output = run(['acpc-solution', 'run', str(source)], input='3000000000 4\n')
|
||||
require('SUM = 3000000004' in output, output)
|
||||
if source.suffix in ('.c', '.cpp'):
|
||||
require('statically linked' in run(['file', str(source.with_suffix(''))]))
|
||||
if source.suffix == '.java':
|
||||
require(int.from_bytes(source.with_suffix('.class').read_bytes()[6:8], 'big') == 69)
|
||||
print(' ' + output.strip().replace('\n', '\n '))
|
||||
check('build and execute ' + filename + ' (path contains spaces)', exercise)
|
||||
|
||||
def driver_flags():
|
||||
log = work / 'compiler.jsonl'
|
||||
for lang, driver, old_standard, expected, library in (
|
||||
('c', 'acpc-gcc', 'c99', 'gnu23', '-lm'),
|
||||
('cpp', 'acpc-g++', 'c++17', 'gnu++23', '-lpthread'),
|
||||
):
|
||||
source = work / ('sum-' + lang) / ('sum.' + lang)
|
||||
run([driver, '-O0', '-std=' + old_standard, str(source), '-o', str(work / driver)],
|
||||
env={**os.environ, 'ACPC_COMPILER_LOG': str(log)})
|
||||
args = json.loads(log.read_text().splitlines()[-1])['argv']
|
||||
require('-O0' not in args and '-O2' in args and '-std=' + expected in args, str(args))
|
||||
require('-static' in args and library in args, str(args))
|
||||
require('15.2.0' in run(['acpc-gcc', '-dumpfullversion']))
|
||||
require('15.2.0' in run(['acpc-g++', '--version']))
|
||||
output = run(['acpc-cc', '-x', 'c++', '-dM', '-E', '-'], input='')
|
||||
require('#define __cplusplus 202302L' in output, 'C++ compiler probes must use the C++ backend')
|
||||
response = work / 'compiler probe.args'
|
||||
response.write_text('-x c++ -std=c++17 -dM -E -')
|
||||
output = run(['acpc-cc', '@' + str(response)], input='')
|
||||
require('#define __cplusplus 202302L' in output,
|
||||
'CLion response-file probes must see the same C++23 standard as compilation')
|
||||
check('compiler drivers override generic project flags, link statically and answer IDE probes', driver_flags)
|
||||
|
||||
def failed_build():
|
||||
case = work / 'sum-cpp'
|
||||
source = case / 'sum.cpp'
|
||||
source.write_text('#error deliberate bad source\n')
|
||||
result = subprocess.run(['acpc-solution', 'run', str(source)], text=True, input='1 2\n', capture_output=True)
|
||||
require(result.returncode != 0 and 'SUM =' not in result.stdout and 'Run:' not in result.stdout,
|
||||
'a failed build must never execute the previous binary')
|
||||
check('failed build does not execute a stale binary', failed_build)
|
||||
|
||||
def geany_commands():
|
||||
for language, filename in [('c', 'sum.c'), ('cpp', 'sum.cpp'), ('java', 'Sum.java'), ('python', 'sum.py')]:
|
||||
conf = configparser.ConfigParser(interpolation=None, strict=False)
|
||||
conf.read('/usr/share/geany/filedefs/filetypes.' + language)
|
||||
source = work / ('geany ' + language) / filename
|
||||
source.parent.mkdir()
|
||||
shutil.copy(samples / filename, source)
|
||||
for key in ('FT_00_CM', 'FT_01_CM', 'EX_00_CM'):
|
||||
command = conf['build-menu'][key].replace('%f', source.name).replace('%e', source.stem)
|
||||
result = run(['bash', '-c', command], cwd=source.parent, input='3000000000 4\n')
|
||||
if key == 'EX_00_CM': require('SUM = 3000000004' in result, result)
|
||||
if key == 'FT_00_CM' and source.suffix in ('.c', '.cpp'):
|
||||
require('relocatable' in run(['file', str(source.with_suffix('.o'))]))
|
||||
check('all four installed Geany build and execute commands work', geany_commands)
|
||||
|
||||
for home in (Path('/home/team'), Path('/etc/skel')):
|
||||
def profiles(home=home):
|
||||
cb = ET.parse(home / '.config/codeblocks/default.conf')
|
||||
cb_root = Path(cb.findtext('./compiler/sets/gcc/MASTER_PATH/str')) / 'bin'
|
||||
require((cb_root / cb.findtext('./compiler/sets/gcc/C_COMPILER/str')).resolve() == Path('/usr/local/bin/acpc-cc'))
|
||||
require((cb_root / cb.findtext('./compiler/sets/gcc/CPP_COMPILER/str')).resolve() == Path('/usr/local/bin/acpc-cc'))
|
||||
require(not Path(cb.findtext('./compiler/sets/gcc/C_COMPILER/str')).is_absolute(),
|
||||
'Code::Blocks prefixes MASTER_PATH/bin, including for an absolute filename')
|
||||
code = home / '.config/Code/User'
|
||||
tasks = json.loads((code / 'tasks.json').read_text())['tasks']
|
||||
require(any(t['label'] == 'ACPC: Build and run' and t['command'] == '/usr/local/bin/acpc-solution' for t in tasks))
|
||||
require(any(b.get('key') == 'f6' and b.get('args') == 'ACPC: Build and run' for b in json.loads((code / 'keybindings.json').read_text())))
|
||||
for product in ('CLion', 'PyCharm'):
|
||||
options = next((home / '.config/JetBrains').glob(product + '*/options'))
|
||||
require('/usr/local/bin/pypy3' in (options / 'jdk.table.xml').read_text())
|
||||
options = next((home / '.config/JetBrains').glob('IntelliJIdea*/options'))
|
||||
require('ACPC Java 25' in (options / 'jdk.table.xml').read_text())
|
||||
require('-XX:+UseSerialGC' in (options / 'project.default.xml').read_text())
|
||||
options = next((home / '.config/JetBrains').glob('CLion*/options'))
|
||||
chain = ET.parse(options / 'linux/toolchains.xml').find('./component/toolchains/toolchain')
|
||||
require(chain.get('customCCompilerPath') == '/usr/local/bin/acpc-gcc')
|
||||
require(chain.get('customCXXCompilerPath') == '/usr/local/bin/acpc-g++')
|
||||
workspace = home / 'eclipse-workspace/.metadata/.plugins'
|
||||
launch = ET.parse(workspace / 'org.eclipse.debug.core/.launches/ACPC Build and run.launch')
|
||||
require(any(n.get('value') == '/usr/local/bin/acpc-solution' for n in launch.getroot()))
|
||||
prefs = workspace / 'org.eclipse.core.runtime/.settings'
|
||||
require('/usr/local/bin/pypy3' in (prefs / 'org.python.pydev.prefs').read_text())
|
||||
require('java-25-openjdk' in (prefs / 'org.eclipse.jdt.launching.prefs').read_text())
|
||||
check(str(home) + ': settings for every IDE are present and parseable', profiles)
|
||||
|
||||
print(f'\n{passed} passed, {failed} failed')
|
||||
raise SystemExit(bool(failed))
|
||||
Reference in new issue
Block a user