-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update to Gradle 8 #3966
Update to Gradle 8 #3966
Conversation
5eed2ca
to
612fe4e
Compare
* Use Java 17 as the main toolchain * Use Java 8 target for the compatibility
* Dokka implicitly uses tasks outputs without dependency * Since Gradle 8, it's an error because implicit dependency execution heavily depends on the tasks order * Make an implicit dependency explicit
* Restore System.out eagerly to avoid chaining failures * Add CountDownLatch to the list of excluded Java classes
If the thread is in 'NEW' state, then 'isAlive: false' and 'join' returns immediately, meaning that there is a race between thread start and shutdown sequence that makes post-join assertion sporadically fail
In order to properly add jvm argument, we have to check jdk toolchain version (where actual tasks are executed) instead of current JDK version (where Gradle is configured)
...where our hero slowly descends into madness
* We are never using it ourselves, yet it is *some* compatibility burden * It is also excluded from aggregate builds thus does not test anything
93fcadf
to
ca9e962
Compare
…to the dedicated convention * Document where appropriate * Verify that knitCheck passes * Also, move dokka.gradle.kts into convention to have a proper IDE support
308cc15
to
3e8e751
Compare
They are no longer needed after migration to JdkToolchain
* Use Gradle 8.5 assignment operator instead of by infix operator * Get rid of Properties.kt * Add some commentary here and there * Cleanups and more Kotlin-ish code * Reduce the number of warnings
@@ -16,17 +17,26 @@ configure(subprojects) { | |||
apiVersion = getOverriddenKotlinApiVersion(project) | |||
if (isMainTaskName && versionsAreNotOverridden) { | |||
allWarningsAsErrors = true | |||
freeCompilerArgs = freeCompilerArgs + "-Xexplicit-api=strict" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, as we are here, AFAIK kotlinOptions
will be deprecated soon-ish and can be replaced already with compilerOptions
which has API using Gradle providers instead of just getters/setters
Maybe it make sense to replace those usages in this PR? (maybe also in other places, if they exist)
Or even move this configuration from tasks to compilations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this in some of the more stable places, but kept it as is in the code that will yet go through rewrites.
@@ -28,24 +28,24 @@ dependencies { | |||
|
|||
|
|||
testImplementation("org.smali:baksmali:${version("baksmali")}") | |||
"r8"("com.android.tools.build:builder:7.1.0-alpha01") | |||
"r8"("com.android.tools.build:builder:8.1.0") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FTR this change reduced the dex size from 79824
to 75120
.
It will affect our stats, it is expected
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's far from done, but there doesn't seem to be too much low-hanging fruit left, and in any case, I think this is already a significant improvement.
val optimizedDexFile = File(optimizedDexDir, "classes.dex") | ||
val unOptimizedDexFile = File(unOptimizedDexDir, "classes.dex") | ||
val optimizedDexFile = optimizedDexDir.map { it.dir("classes.dex") } .get().asFile | ||
val unOptimizedDexFile = unOptimizedDexDir.map { it.dir("classes.dex") }.get().asFile |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't get
here hinder configuration avoidance?
@@ -16,17 +17,26 @@ configure(subprojects) { | |||
apiVersion = getOverriddenKotlinApiVersion(project) | |||
if (isMainTaskName && versionsAreNotOverridden) { | |||
allWarningsAsErrors = true | |||
freeCompilerArgs = freeCompilerArgs + "-Xexplicit-api=strict" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this in some of the more stable places, but kept it as is in the code that will yet go through rewrites.
I don't know where to put it appropriately, so I'll write it here for now: # Usage:
# rm -rf ~/.m2
# ./gradlew clean publishToMavenLocal
# python check_module_info.py
from pathlib import Path
from shutil import rmtree
import subprocess
import os
version_property_name="version"
version = None
for line in open('gradle.properties'):
if line.startswith(f"{version_property_name}="):
version = line.removeprefix(f"{version_property_name}=").rstrip()
break
if version is None:
raise AssertionError(
f"Didn't find '{version_property_name}=' in gradle.properties"
)
modules_with_module_info = [
("", "kotlinx-coroutines-core", "jvm"),
("ui", "kotlinx-coroutines-javafx", ""),
("ui", "kotlinx-coroutines-android", ""),
("ui", "kotlinx-coroutines-swing", ""),
("", "kotlinx-coroutines-test", "jvm"),
("", "kotlinx-coroutines-debug", ""),
("reactive", "kotlinx-coroutines-reactive", ""),
("reactive", "kotlinx-coroutines-jdk9", ""),
("reactive", "kotlinx-coroutines-reactor", ""),
("reactive", "kotlinx-coroutines-rx3", ""),
("reactive", "kotlinx-coroutines-rx2", ""),
]
m2_path=str(Path.home().joinpath(Path('.m2')))
class ModuleInfo:
def __init__(
self,
name: bytes,
exports: set[bytes],
requires: set[(bytes, bytes)],
provides: set[(bytes, bytes)],
uses: set[bytes],
contains: set[bytes],
):
self.name = name
self.exports = exports
self.requires = requires
self.provides = provides
self.uses = uses
def __eq__(self, other):
return (self.name == other.name and
self.exports == other.exports and
self.requires == other.requires and
self.provides == other.provides and
self.uses == other.uses)
def __str__(self):
return f'{self.name}[exports {self.exports}, requires {self.requires}, provides {self.provides}, uses {self.uses}]'
def __repr__(self):
return f'ModuleInfo({self.name}, {self.exports}, {self.requires}, {self.provides}, {self.uses})'
def jar_describe(module: str, artifact: str) -> [ModuleInfo]:
description = subprocess.run(
["jar", "--describe-module", "--file", artifact, "--release", "9"],
stdout = subprocess.PIPE
)
lines = description.stdout.split(b'\n')
if lines[0] != b'releases: 9': raise AssertionError(f'Expected the dump to start with b"releases: 9", got {lines[0]}')
if lines[1] != b'': raise AssertionError(f'Expected an empty line, got {lines[1]}')
jvmModule = module.replace('-', '.')
i = 2
module_info = bytes(f'jar:file:https://{artifact}/!META-INF/versions/9/module-info.class', 'utf-8')
infos = []
def read_header(line: bytes) -> bytes:
name, current_module_info = line.split(b' ')
if current_module_info != module_info: raise AssertionError(f'Expected a header with the module-info {module_info}, got {line}')
return name
while i < len(lines) - 1:
module_name = read_header(lines[i])
expected_third_line=bytes(f'{jvmModule} jar:file:https://{artifact}/!META-INF/versions/9/module-info.class', 'utf-8')
if lines[i] != expected_third_line: raise AssertionError(f'Expected the header {expected_third_line}, got {lines[i]}')
current_line = None
i += 1
exports = set()
requires = set()
provides = set()
uses = set()
contains = set()
while lines[i] != b'':
line = lines[i]
operation, *rest = line.split()
try:
if operation == b'exports':
[package] = rest
exports.add(package)
elif operation == b'requires':
if len(rest) == 1:
[package] = rest
modifier = None
else:
package, modifier = rest
requires.add((package, modifier))
elif operation == b'provides':
[service, wth, cls] = rest
if wth != b'with': raise AssertionError(f'Unexpected syntax for "provides"')
provides.add((service, cls))
elif operation == b'uses':
[package] = rest
uses.add(package)
elif operation == b'contains':
[package] = rest
contains.add(package)
else:
raise AssertionError(f'Unknown operation')
except:
print(f'An error on line {line}')
raise
i += 1
infos.append(
ModuleInfo(module_name, exports, requires, provides, uses, contains)
)
i += 1
return infos
def read_module_info(module_info: str) -> ModuleInfo:
infos = []
mapping = dict()
with open(module_info, 'rb') as fl:
content = fl.read().replace(b'@SuppressWarnings("JavaModuleNaming")', b'')
new_lines = []
for line in content.split(b'\n'):
before_comment, *rest = line.split(b'//')
new_lines.append(before_comment)
content = b''.join(new_lines)
lines = content.replace(b'{', b';{;').replace(b'}', b';};').split(b';')
new_lines = []
for line in lines:
line = line.strip()
if line == b'': continue
new_lines.append(line)
lines = new_lines
i = 0
while i < len(lines):
line = lines[i]
i += 1
try:
operation, argument = line.split()
if operation == b'import':
name = argument.split(b'.')[-1]
mapping[name] = argument
elif operation == b'module':
module_name = argument
exports = set()
requires = set([(b'java.base', b'mandated')])
provides = set()
uses = set()
contains = set()
if lines[i] != b'{': raise AssertionError('Expected an "{"')
i += 1
while lines[i] != b'}':
line = lines[i]
operation, *rest = line.split()
if operation == b'exports':
[package] = rest
exports.add(package)
elif operation == b'requires':
if len(rest) == 1:
[package] = rest
modifier = None
else:
modifier, package = rest
if package != b'kotlinx.atomicfu' or modifier != None:
requires.add((package, modifier))
elif operation == b'provides':
[cls, wth, service] = rest
if wth != b'with': raise AssertionError(f'Unexpected syntax for "provides"')
provides.add((mapping.get(cls, cls), mapping.get(service, service)))
elif operation == b'uses':
[cls] = rest
uses.add(mapping.get(cls, cls))
elif operation == b'contains':
[package] = rest
contains.add(package)
else:
raise AssertionError(f'Unknown operation')
i += 1
i += 1
infos.append(
ModuleInfo(module_name, exports, requires, provides, uses, contains)
)
else:
raise AssertionError('Unknown operation')
except:
print(f'An error on line {i} of {module_info}: {line}')
raise
return infos
for (prefix, module, platform) in modules_with_module_info:
artifact_name=f'{module}{"-" + platform if platform else ""}'
print(f'Checking {artifact_name}...')
from_jar_describe = jar_describe(module, f'{m2_path}/repository/org/jetbrains/kotlinx/{artifact_name}/{version}/{artifact_name}-{version}.jar')
from_file = read_module_info(f'{prefix or "."}/{module}{"/" + platform if platform else ""}/src/module-info.java')
if from_file != from_jar_describe: raise AssertionError(f'Expected {from_file} and {from_jar_describe} to be the same')
print("OK") The manual check of module-info is done:
|
65c1667
to
583f10a
Compare
The version files are also all there: $ find ~/.m2 -name 'kotlinx-coroutines-*SNAPSHOT.jar' | xargs -n1 unzip -l | grep -F .version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_reactive.version
17 1980-02-01 00:00 META-INF/kotlinx_coroutines_core.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_jdk8.version
17 1980-02-01 00:00 META-INF/kotlinx_coroutines_test.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_javafx.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_jdk9.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_reactor.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_rx3.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_play_services.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_slf4j.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_rx2.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_debug.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_guava.version
17 2023-12-15 14:19 META-INF/kotlinx_coroutines_android.version
17 2023-12-15 14:21 META-INF/kotlinx_coroutines_swing.version |
* We have no Java 17 on our agents * Downloading toolchain can only be configured with a custom third-party plugin that we don't want to depend on
de3cdcb
to
4509e20
Compare
6f032a3
to
bfa22e9
Compare
Checked the classfile versions. All of the classes that we publish have major version 52, except the following ones:
echo 'Extracting...'
for dir in ~/.m2/repository/org/jetbrains/kotlinx/kotlinx-coroutines*; do
find "$dir" -name '*.jar' -exec unzip -o -q '{}' ';'
done
echo 'Checking every classfile...'
find -name '*.class' -print0 |
xargs -0 -n1 sh -c 'printf "%s: %s\n" "$0" "$(javap -verbose "$0" | grep "major version:")"' |
grep -v ': 52' |
* Remove the workarounds for IDEA import from gradle.kts Without the workarounds, everything still seems to be working fine, even after invalidating the caches. * Clarify the opt-ins. Unify the handling of all opt-ins in the project, and also add opt-ins for Native that would be widespread otherwise. Unfortunately, even after that, the IDE still complains about not having the right opt-in in kotlinx-coroutines-test/common/test/Helpers.kt. * Extract the logic of grouping several source sets into a single shared one. * Update the kotlinx-coroutines-core build script: - Utilize the multiplatform hierarchy to avoid defining extra source sets. - Reorder the code for readability. * Fix the IDE failing to find `actual` implementations for jvmCore by making sure `jvmCoreMain` is only defined when the IDE is not active. Without a dependency from jvmCoreMain to jvmMain, `expect` declarations in the IDE complained that they couldn't find `actual` in the `jvmCore` source-set. For example, this could be seen in kotlinx-coroutines-core/common/src/internal/Concurrent.common.kt. * Fix passing the stressTest system property to tests Broken in #3966 * kotlinOptions -> compilerOptions
Update to Gradle 8.
Each commit should be more or less self-describing and [potentially] strightforward.
Note that this shouldn't be merged until all the required checks are performed:
module-info
manual checks