-
Notifications
You must be signed in to change notification settings - Fork 39
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
Migrate from core native to Nokee plugins #298
Migrate from core native to Nokee plugins #298
Conversation
92f458f
to
1e05f9c
Compare
@big-guy At the moment, the branch is using a local version so the CI will fail. I will cut a release for you once our CI stabilizes. |
@@ -30,6 +32,10 @@ gradlePlugin { | |||
id = "gradlebuild.jni" | |||
implementationClass = "gradlebuild.JniPlugin" | |||
} | |||
jniNokee { | |||
id = "gradlebuild.jni-nokee" |
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.
💄 This is a temporary plugin that will replace the original gradlebuild.jni
before merging.
|
||
import org.gradle.api.Named; | ||
|
||
public enum WindowsDistribution implements Named { |
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.
🔍 The WindowsDistribution refers here to the WINDOWS_MIN
which is in fact a way to differentiate between XP and lower vs Vista and higher.
|
||
@Override | ||
public String getName() { | ||
return name(); |
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.
❓ We will need to figure out the correct name to use here. Nokee uses the name of the dimension value to generate names (tasks, variants, etc.). We could use min
and empty string to ensure the names aligns with the software model plugins but the empty string can be problematic as it can cause domain object collision. At the moment Nokee doesn't disallow it which is an open question and definitely not a defined behaviour.
$.platforms.each { p -> | ||
if (p.name.contains("ncurses")) { | ||
return | ||
} | ||
targetPlatform p.name | ||
} |
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.
❓ The targetMachines
are configured in the JniNokeePlugin
which is a behaviour change. With the software model plugins, platforms
need to be declared before using them. With Nokee, we simply create the target machine. We may want to consider a helper method that contains all of the default target machines and set the value here to make it more explicit.
headers.from('src/shared/headers', 'build/generated/jni-headers/main') | ||
} | ||
variants.configureEach { | ||
if (NcursesVersion.values().any { buildVariant.hasAxisOf(it) }) { |
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.
❓ 💄 We don't provide a way to check if an axis is present. Instead, queries are done by axis value. It could be useful to query the presence of an axis here however there are some additional open questions to consider.
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.
Do you have a link to the latest docs for what the backing object for library
and the element of variants
?
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 a little bit out of date but it should give you a good enough idea: https://docs.nokee.dev/dsl/dev.nokee.platform.jni.JavaNativeInterfaceLibrary.html
result.add(machines.os("osx").architecture("amd64")); | ||
result.add(machines.os("osx").architecture("aarch64")); | ||
result.add(machines.getLinux().architecture("amd64")); | ||
result.add(machines.getLinux().architecture("aarch64")); | ||
result.add(machines.getWindows().architecture("i386")); | ||
result.add(machines.getWindows().architecture("amd64")); |
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.
🔍 We use osx
and amd64
instead of the factory method getMacOs()
and getX86_64()
respectively simply to honour the previous naming. Both ad-hoc values are understood correctly. Using the canonical name would revert to stable names.
private void configureCppTasks(Project project) { | ||
TaskContainer tasks = project.getTasks(); | ||
TaskProvider<JavaCompile> compileJavaProvider = tasks.named("compileJava", JavaCompile.class); | ||
tasks.withType(CppCompile.class) | ||
.configureEach(task -> task.includes( | ||
compileJavaProvider.flatMap(it -> it.getOptions().getHeaderOutputDirectory()) | ||
)); | ||
} |
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.
🔍 We still wire the generated JNI headers manually because of two reasons: 1) the convention on Nokee source set needs to be reviewed and 2) the native sources are wired directly into the writeNativeVersionSources
which the output are wired back to the compileJava
causing a circular dependency.
Adding any additional files to the source set will override the convention. By default, the headers conversion for a JNI library with C++ sources will be src/<componentName>/headers
and build/generated/jni-headers/<componentName>
. When configuring the project, we mostly intend to add src/shared/headers
instead of overwriting the current headers. However, given the circular dependency, the intention may be correct.
We could add an extra C source set just for the JNI headers but that would not be the correct intention. We need the JNI headers to compile any JNI native sources (C++).
I believe the ideal scenario would be to allow more flexibility in the source set so we can build upon the convention and filter the generated sources when configuring writeNativeVersionSources
.
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.
IIUC, we're doing this to ourselves.
- We take the Java code and generate headers
- We take the generated headers and create a hash. We put that into another generated header.
So to get the "set of generated headers" to generate the last header, we need something in between the full set of generated headers and the hand written stuff.
// The core Gradle toolchain for Clang only targets x86 and x86_64 out of the box. | ||
OperatingSystem os = new DefaultNativePlatform("current").getOperatingSystem(); | ||
if (os.isMacOsX()) { | ||
toolChain.target("macosaarch64"); |
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.
❓ Target machines to target platform mapping use the canonical name. It seemed like a good idea at the time but in this context, it looks a bit strange. The target machine was declared using osx
and we use macos
here. The initial reason was to allow toolchain definition sharing regardless of the name chosen for the OS family and architecture. The reason is still very much valid.
import java.util.Set; | ||
|
||
@SuppressWarnings("UnstableApiUsage") | ||
public abstract class JniNokeePlugin implements Plugin<Project> { |
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.
❓ Is there a reason the JNI tests are run as part of another Test
task? Nokee wires everything for the main Test
task. There wasn't any consideration to "remove" the wiring of the JNI into the main test task. We can create a new test task but all the wiring will have to be done manually. Nokee could provide a jni-unit-test
plugin that allows users to declare any number of JNI test tasks but that is a bit outside of the current scope. The project could remove the extra test task in favour of the main test task however there may be a reason for the task to existing in the first place.
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.
We have some special tests where -Xcheck:jni
output is treated as a reason for failure. These are the tests marked with JniChecksEnabled
. For other tests that were introduced earlier, we did not enforce these checks yet, and they do fail.
One option to remove this distinction would be to make sure none of the current code emits any JNI warnings.
@big-guy The dependency verification is quite unhelpful. Locally, it fails with an |
a436c79
to
73c1d92
Compare
private void registerWindowsDistributionDimension(JavaNativeInterfaceLibrary library) { | ||
SetProperty<WindowsDistribution> newDimension = library.getDimensions().newAxis(WindowsDistribution.class, builder -> builder.onlyOn(library.getMachines().getWindows().getOperatingSystemFamily())); | ||
newDimension.convention(ImmutableSet.copyOf(WindowsDistribution.values())); | ||
((ExtensionAware) library).getExtensions().add("targetWindowsDistributions", newDimension); |
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.
🔍 See conversation in the #ext-nokee
Slack channel around ExtensionAware
guideline.
Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
As of this commit, the axis is not present so the configuration has no effect. Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
The Ncurses dimension is not configure as of this commit. However, the configuration will be the same. Signed-off-by: Daniel Lacasse <[email protected]>
We manually write the generated JNI headers to the C++ compile task instead of using the C++ source set as there seems to be a cycle in the configuration that uses writeNativeVersionSources. It's most likely due to up-to-date checks. Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
They are wired in from within the JniNokeePlugin. Signed-off-by: Daniel Lacasse <[email protected]>
We also changed the named for the enum values so it better reflect the original naming. Signed-off-by: Daniel Lacasse <[email protected]>
Signed-off-by: Daniel Lacasse <[email protected]>
b49d713
to
df95186
Compare
@big-guy I released an integration version. We still need to figure out the dependency verification error for CI to be happy. |
headers.from('src/shared/headers', 'build/generated/jni-headers/main') | ||
} | ||
variants.configureEach { | ||
if (NcursesVersion.values().any { buildVariant.hasAxisOf(it) }) { |
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.
Do you have a link to the latest docs for what the backing object for library
and the element of variants
?
private void configureCppTasks(Project project) { | ||
TaskContainer tasks = project.getTasks(); | ||
TaskProvider<JavaCompile> compileJavaProvider = tasks.named("compileJava", JavaCompile.class); | ||
tasks.withType(CppCompile.class) | ||
.configureEach(task -> task.includes( | ||
compileJavaProvider.flatMap(it -> it.getOptions().getHeaderOutputDirectory()) | ||
)); | ||
} |
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.
IIUC, we're doing this to ourselves.
- We take the Java code and generate headers
- We take the generated headers and create a hash. We put that into another generated header.
So to get the "set of generated headers" to generate the last header, we need something in between the full set of generated headers and the hand written stuff.
} else { | ||
return ImmutableList.of(toVariantName(it.getTargetMachine())); | ||
} | ||
})); |
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.
What does it mean to set the "variant names" here? This disables certain variants from running?
} | ||
}); | ||
library.getVariants().configureEach(variant -> { | ||
variant.getResourcePath().set(String.join("/", |
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.
What's this resource path relative to?
toolChainRegistry.create("gcc", Gcc.class, toolChain -> { | ||
// The core Gradle toolchain for gcc only targets x86 and x86_64 out of the box. | ||
// https://github.com/gradle/gradle/blob/36614ee523e5906ddfa1fed9a5dc00a5addac1b0/subprojects/platform-native/src/main/java/org/gradle/nativeplatform/toolchain/internal/gcc/AbstractGccCompatibleToolChain.java | ||
toolChain.target("linuxaarch64"); |
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.
is there an issue for this?
Fixes #297.
See the attached issues for general work items left to do. Feel free to provide any kind of feedback on this PR either by starting a new conversation or contributing to a current conversation.