Skip to content
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

Merged

Conversation

lacasseio
Copy link

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.

@lacasseio
Copy link
Author

@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"
Copy link
Author

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 {
Copy link
Author

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();
Copy link
Author

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.

Comment on lines -27 to -32
$.platforms.each { p ->
if (p.name.contains("ncurses")) {
return
}
targetPlatform p.name
}
Copy link
Author

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) }) {
Copy link
Author

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.

Copy link
Member

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?

Copy link
Author

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

Comment on lines +138 to +152
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"));
Copy link
Author

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.

Comment on lines +127 to +143
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())
));
}
Copy link
Author

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.

Copy link
Member

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");
Copy link
Author

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> {
Copy link
Author

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.

Copy link
Member

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.

@lacasseio
Copy link
Author

@big-guy The dependency verification is quite unhelpful. Locally, it fails with an Invalid UTF-8 error which is not clear as to what's the problem. I removed the verification files locally until we can work around this issue.

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);
Copy link
Author

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.

As of this commit, the axis is not present so the configuration has no
effect.

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]>
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]>
@lacasseio
Copy link
Author

@big-guy I released an integration version. We still need to figure out the dependency verification error for CI to be happy.

@big-guy big-guy changed the base branch from master to nokee-migration December 23, 2021 14:13
@big-guy big-guy merged commit 8054695 into gradle:nokee-migration Jan 5, 2022
headers.from('src/shared/headers', 'build/generated/jni-headers/main')
}
variants.configureEach {
if (NcursesVersion.values().any { buildVariant.hasAxisOf(it) }) {
Copy link
Member

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?

Comment on lines +127 to +143
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())
));
}
Copy link
Member

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()));
}
}));
Copy link
Member

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("/",
Copy link
Member

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");
Copy link
Member

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Migrate from core native support to Nokee plugins
3 participants