|
#!/usr/bin/env python3 |
|
import sys |
|
import hashlib |
|
import os |
|
import subprocess |
|
from datetime import datetime |
|
import itertools |
|
|
|
filename = sys.argv[-1] |
|
with open(filename, 'r') as f: |
|
all_code = f.read() |
|
|
|
content_hash = hashlib.sha256(all_code.encode()).hexdigest() |
|
scala_code = all_code[1:] |
|
|
|
cache_path = os.path.expanduser(f"~/.ivy2/local/com.abdulradi.scala-scripts-launcher/generated/{content_hash}") |
|
launcher_path = f'{cache_path}/launcher' |
|
|
|
if not os.path.exists(launcher_path): |
|
dependencies = list(itertools.takewhile(lambda x: not x.startswith('-'), sys.argv[1:-1])) |
|
bootstrap_flags = ' '.join(itertools.dropwhile(lambda x: not x.startswith('-'), sys.argv[1:-1])) |
|
now = datetime.now().timestamp() |
|
|
|
os.makedirs(f'{cache_path}/jars', exist_ok=True) |
|
os.makedirs(f'{cache_path}/ivys', exist_ok=True) |
|
|
|
# Copy of scala code with shebang commented out as it confuses scalac. TODO: Find a better place for this file |
|
scalacode_path = f'{cache_path}/jars/generated.scala' |
|
with open(scalacode_path, "w") as f: |
|
f.write(f'//{all_code}') |
|
|
|
jar_path = f'{cache_path}/jars/generated.jar' |
|
class_path = subprocess.check_output(['coursier', 'fetch', '--classpath'] + dependencies).decode('utf-8') |
|
|
|
os.system(f'coursier launch scalac -- {scalacode_path} -d {jar_path} -classpath {class_path} ') |
|
scala_version = subprocess.check_output(['coursier', 'launch', 'scalac', '--', '-version']).decode('utf-8')[23:-55] # Drop the copyrights and extract version number, probably very fragile and might break with dotty |
|
[scala_version_major, scala_version_minor, scala_version_patch] = scala_version.split('.') |
|
with open(f'{cache_path}/ivys/ivy.xml', "w") as f: |
|
f.write('<?xml version="1.0" encoding="UTF-8"?>\n') |
|
f.write('<ivy-module version="2.0" xmlns:e="http://ant.apache.org/ivy/extra">\n') |
|
f.write(f' <info organisation="com.abdulradi.scala-scripts-launcher" module="generated" revision="{content_hash}" status="integration" publication="{now}"></info>\n') |
|
f.write(' <configurations>\n') |
|
f.write(' <conf name="compile" visibility="public" description=""/>\n') |
|
f.write(' <conf name="optional" visibility="public" description=""/>\n') |
|
f.write(' <conf name="scala-tool" visibility="private" description=""/>\n') |
|
f.write(' </configurations>\n') |
|
f.write(' <dependencies>\n') |
|
f.write(f' <dependency org="org.scala-lang" name="scala-compiler" rev="{scala_version}" conf="scala-tool->default,optional(default)"/>\n') |
|
f.write(f' <dependency org="org.scala-lang" name="scala-library" rev="{scala_version}" conf="scala-tool->default,optional(default);compile->default(compile)"/>\n') |
|
for dependency in dependencies: |
|
if '::' in dependency: |
|
[org, name_rev] = dependency.split('::') |
|
[name_unversioned, rev] = name_rev.split(':') |
|
name = f'{name_unversioned}_{scala_version_major}.{scala_version_minor}' |
|
else: |
|
[org, name, rev] = dependency.split(':') |
|
|
|
f.write(f' <dependency org="{org}" name="{name}" rev="{rev}" conf="compile->default(compile)"/>\n') |
|
|
|
f.write(' </dependencies>\n') |
|
f.write('</ivy-module>\n') |
|
|
|
os.system(f'coursier bootstrap com.abdulradi.scala-scripts-launcher:generated:{content_hash} {bootstrap_flags} -o {launcher_path} --quiet') |
|
|
|
|
|
os.system(launcher_path) |
I want to use Coursier in a similar way in https://github.com/tsk-tsk/tsk-tsk, as an opt-in, because the native compilation may take long.