mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Kotlin/Bazel: provide wrapper for managing versions of kotlinc
By adding `java/kotlinc-extractor/deps/dev` to `PATH`, one gets a `kotlinc` wrapper that takes care of downloading and extracting the desired version of `kotlinc` on demand. The desired version can be selected with `kotlinc --select x.y.z`, or left to the current default of `1.9.0`. Moreover, this default version is integrated with the Bazel build, so that when using this wrapper, changes in the selected version will be picked up to define the default single version kotlin extractor build, without needing to do anything else (like `bazel fetch --force` or similar). Selected and installed version data is stored in `.gitignore`d files in the same directory, and can be cleared with `kotlinc --clear`.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
load("//java/kotlin-extractor:versions.bzl", "DEFAULT_VERSION", "VERSIONS", "version_less")
|
||||
load("//java/kotlin-extractor:versions.bzl", "VERSIONS", "version_less")
|
||||
load("//misc/bazel:lfs.bzl", "lfs_smudge")
|
||||
|
||||
_kotlin_dep_build = """
|
||||
@@ -73,11 +73,22 @@ def _get_default_version(repository_ctx):
|
||||
default_version = repository_ctx.getenv("CODEQL_KOTLIN_SINGLE_VERSION")
|
||||
if default_version:
|
||||
return default_version
|
||||
if not repository_ctx.which("kotlinc"):
|
||||
return DEFAULT_VERSION
|
||||
kotlin_plugin_versions = repository_ctx.path(Label("//java/kotlin-extractor:current_kotlin_version.py"))
|
||||
python = repository_ctx.which("python3") or repository_ctx.which("python")
|
||||
res = repository_ctx.execute([python, kotlin_plugin_versions])
|
||||
env = {}
|
||||
repository_ctx.watch(Label("//java/kotlin-extractor/deps:dev/.kotlinc_selected_version"))
|
||||
if not repository_ctx.which("kotlinc"):
|
||||
# take default from the kotlinc wrapper
|
||||
path = repository_ctx.getenv("PATH")
|
||||
path_to_add = repository_ctx.path(Label("//java/kotlin-extractor/deps:dev"))
|
||||
if not path:
|
||||
path = str(path_to_add)
|
||||
elif repository_ctx.os.name == "windows":
|
||||
path = "%s;%s" % (path, path_to_add)
|
||||
else:
|
||||
path = "%s:%s" % (path, path_to_add)
|
||||
env["PATH"] = path
|
||||
res = repository_ctx.execute([python, kotlin_plugin_versions], environment = env)
|
||||
if res.return_code != 0:
|
||||
fail(res.stderr)
|
||||
return res.stdout.strip()
|
||||
|
||||
3
java/kotlin-extractor/deps/dev/.gitignore
vendored
Normal file
3
java/kotlin-extractor/deps/dev/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/.kotlinc_installed
|
||||
/.kotlinc_installed_version
|
||||
/.kotlinc_selected_version
|
||||
156
java/kotlin-extractor/deps/dev/kotlinc
Executable file
156
java/kotlin-extractor/deps/dev/kotlinc
Executable file
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Wrapper script that manages kotlinc versions.
|
||||
Usage: add this directory to your PATH, then
|
||||
* `kotlinc --select x.y.z` will select the version for the next invocations, checking it actually exists
|
||||
* `kotlinc --clear` will remove any state of the wrapper (deselecting a previous version selection)
|
||||
* `kotlinc -version` will print the selected version information. It will not print `JRE` information as a normal
|
||||
`kotlinc` invocation would do though. In exchange, the invocation incurs no overhead.
|
||||
* Any other invocation will forward to the selected kotlinc version, downloading it if necessary. If no version was
|
||||
previously selected with `--select`, a default will be used (see `DEFAULT_VERSION` below)
|
||||
|
||||
In order to install kotlin, ripunzip will be used if installed, or if running on Windows within `semmle-code` (ripunzip
|
||||
is available in `resources/lib/windows/ripunzip` then).
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import os
|
||||
import urllib
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import argparse
|
||||
import sys
|
||||
import platform
|
||||
import subprocess
|
||||
import zipfile
|
||||
import shutil
|
||||
import io
|
||||
|
||||
DEFAULT_VERSION = "1.9.0"
|
||||
|
||||
def options():
|
||||
parser = argparse.ArgumentParser(add_help=False)
|
||||
parser.add_argument("--select")
|
||||
parser.add_argument("--clear", action="store_true")
|
||||
parser.add_argument("-version", action="store_true")
|
||||
return parser.parse_known_args()
|
||||
|
||||
|
||||
url_template = 'https://github.com/JetBrains/kotlin/releases/download/v{version}/kotlin-compiler-{version}.zip'
|
||||
this_dir = pathlib.Path(__file__).resolve().parent
|
||||
version_file = this_dir / ".kotlinc_selected_version"
|
||||
installed_version_file = this_dir / ".kotlinc_installed_version"
|
||||
install_dir = this_dir / ".kotlinc_installed"
|
||||
windows_ripunzip = this_dir.parents[4] / "resources" / "lib" / "windows" / "ripunzip" / "ripunzip.exe"
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ZipFilePreservingPermissions(zipfile.ZipFile):
|
||||
def _extract_member(self, member, targetpath, pwd):
|
||||
if not isinstance(member, zipfile.ZipInfo):
|
||||
member = self.getinfo(member)
|
||||
|
||||
targetpath = super()._extract_member(member, targetpath, pwd)
|
||||
|
||||
attr = member.external_attr >> 16
|
||||
if attr != 0:
|
||||
os.chmod(targetpath, attr)
|
||||
return targetpath
|
||||
|
||||
|
||||
def check_version(version: str):
|
||||
try:
|
||||
with urllib.request.urlopen(url_template.format(version=version)) as response:
|
||||
pass
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code == 404:
|
||||
raise Error(f"Version {version} not found in github.com/JetBrains/kotlin/releases") from e
|
||||
raise
|
||||
|
||||
def get_version(file: pathlib.Path) -> str:
|
||||
try:
|
||||
return file.read_text()
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
|
||||
|
||||
def install(version: str):
|
||||
url = url_template.format(version=version)
|
||||
if install_dir.exists():
|
||||
shutil.rmtree(install_dir)
|
||||
install_dir.mkdir()
|
||||
ripunzip = shutil.which("ripunzip")
|
||||
if platform.system() == "Windows" and windows_ripunzip.exists():
|
||||
ripunzip = windows_ripunzip
|
||||
if ripunzip:
|
||||
print(f"downloading and extracting {url} using ripunzip", file=sys.stderr)
|
||||
subprocess.run([ripunzip, "unzip-uri", url], cwd=install_dir, check=True)
|
||||
return
|
||||
with io.BytesIO() as buffer:
|
||||
with urllib.request.urlopen(url) as response:
|
||||
print(f"downloading {url}", file=sys.stderr)
|
||||
while True:
|
||||
bytes = response.read()
|
||||
if not bytes:
|
||||
break
|
||||
buffer.write(bytes)
|
||||
buffer.seek(0)
|
||||
print(f"extracting kotlin-compiler-{version}.zip", file=sys.stderr)
|
||||
with ZipFilePreservingPermissions(buffer) as archive:
|
||||
archive.extractall(install_dir)
|
||||
|
||||
|
||||
def forward(forwarded_opts):
|
||||
kotlinc = install_dir / "kotlinc" / "bin" / "kotlinc"
|
||||
if platform.system() == "Windows":
|
||||
kotlinc = kotlinc.with_suffix(".bat")
|
||||
args = [sys.argv[0]]
|
||||
args.extend(forwarded_opts)
|
||||
os.execv(kotlinc, args)
|
||||
|
||||
def clear():
|
||||
if install_dir.exists():
|
||||
print(f"removing {install_dir}", file=sys.stderr)
|
||||
shutil.rmtree(install_dir)
|
||||
if installed_version_file.exists():
|
||||
print(f"removing {installed_version_file}", file=sys.stderr)
|
||||
installed_version_file.unlink()
|
||||
if version_file.exists():
|
||||
print(f"removing {version_file}", file=sys.stderr)
|
||||
version_file.unlink()
|
||||
|
||||
def main(opts, forwarded_opts):
|
||||
if opts.clear:
|
||||
clear()
|
||||
return
|
||||
if opts.select:
|
||||
check_version(opts.select)
|
||||
version_file.write_text(opts.select)
|
||||
selected_version = opts.select
|
||||
else:
|
||||
selected_version = get_version(version_file)
|
||||
if not selected_version:
|
||||
selected_version = DEFAULT_VERSION
|
||||
version_file.write_text(selected_version)
|
||||
if opts.version:
|
||||
print(f"info: kotlinc-jvm {selected_version} (codeql dev wrapper)", file=sys.stderr)
|
||||
return
|
||||
if opts.select and not forwarded_opts:
|
||||
print(f"selected {selected_version}", file=sys.stderr)
|
||||
return
|
||||
if get_version(installed_version_file) != selected_version:
|
||||
install(selected_version)
|
||||
installed_version_file.write_text(selected_version)
|
||||
forward(forwarded_opts)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main(*options())
|
||||
except Exception as e:
|
||||
print(f"{e.__class__.__name__}: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
2
java/kotlin-extractor/deps/dev/kotlinc.bat
Normal file
2
java/kotlin-extractor/deps/dev/kotlinc.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
python3 %~dp0/kotlinc
|
||||
@@ -14,8 +14,6 @@ VERSIONS = [
|
||||
"2.0.0-RC1",
|
||||
]
|
||||
|
||||
DEFAULT_VERSION = "1.9.0"
|
||||
|
||||
def _version_to_tuple(v):
|
||||
# we ignore the tag when comparing versions, for example 1.9.0-Beta <= 1.9.0
|
||||
v, _, ignored_tag = v.partition("-")
|
||||
|
||||
Reference in New Issue
Block a user