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

Swift2Java package plugin should fetch dependencies #135

Open
ktoso opened this issue Oct 29, 2024 · 2 comments
Open

Swift2Java package plugin should fetch dependencies #135

ktoso opened this issue Oct 29, 2024 · 2 comments

Comments

@ktoso
Copy link
Collaborator

ktoso commented Oct 29, 2024

If we're intent on making the "write a swift app, use some java libs" workflow real we're going to need to handle dependencies. Very few libraries are just one jar, and managing this by hand is not really realistic.

So, we need to resolve and fetch dependencies. Thankfully, there's libraries which do this, options include:

Our best bet is probably the Maven resolver:

Should be a pretty stable way to get the job done.

We can get a classpath for dependencies:

        RepositorySystem repoSystem = newRepositorySystem();
        RepositorySystemSession session = newSession( repoSystem );
        Dependency dependency =
            new Dependency( new DefaultArtifact( "org.apache.maven:maven-core:3.9.6" ), "compile" );
        RemoteRepository central = new RemoteRepository.Builder( "central", "default", "https://repo.maven.apache.org/maven2/" ).build();
        CollectRequest collectRequest = new CollectRequest();
        collectRequest.setRoot( dependency );
        collectRequest.addRepository( central );
        DependencyNode node = repoSystem.collectDependencies( session, collectRequest ).getRoot();
        DependencyRequest dependencyRequest = new DependencyRequest();
        dependencyRequest.setRoot( node );
        repoSystem.resolveDependencies( session, dependencyRequest  );
        PreorderNodeListGenerator nlg = new PreorderNodeListGenerator();
        node.accept( nlg );
        System.out.println( nlg.getClassPath() );

Other options

Ivy is an option, sbt used to use this, but I'm not sure how good it has been maintained.

Which has very minimal API:

import coursier._

val files = Fetch()
  .addDependencies(dep"org.tpolecat:doobie-core_2.12:0.6.0")
  .run()

but it'd pull in Scala...

@ktoso
Copy link
Collaborator Author

ktoso commented Oct 29, 2024

Ugh, so the eclipse/maven APIs are a bit of a pain to work with... maybe we can work it out though. Or look into the alternatives.

It might be also worth looking into using Gradle as an API

@ktoso
Copy link
Collaborator Author

ktoso commented Oct 29, 2024

The gradle API does still require we create a project to work on it seems. We could use a virtual file system though.

plugins {
    id("java")
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    implementation("dev.gradleplugins:gradle-api:8.10.1")
}

tasks.test {
    useJUnitPlatform()
}
import org.gradle.tooling.GradleConnector;

import java.io.*;
import java.nio.file.Files;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws IOException {

        String[] dependencies = {
                "org.apache.commons:commons-lang3:3.12.0",
                "com.google.guava:guava:33.3.1-jre",
                "com.fasterxml.jackson.core:jackson-databind:2.18.0",
        };

        var classpath = getClasspathWithDependency(dependencies);
        System.out.println("classpath = " + classpath);
    }

    /**
     * May throw runtime exceptions including {@link org.gradle.api.internal.artifacts.ivyservice.TypedResolveException}
     * if unable to resolve a dependency.
     */
    private static String getClasspathWithDependency(String[] dependencies) throws IOException {
        File projectDir = Files.createTempDirectory("java-swift-dependencies").toFile();
        projectDir.mkdirs();

        File buildFile = new File(projectDir, "build.gradle");
        try (PrintWriter writer = new PrintWriter(buildFile)) {
            writer.println("plugins { id 'java-library' }");
            writer.println("repositories { mavenCentral() }");

            writer.println("dependencies {");
            for (String dependency : dependencies) {
                writer.println("implementation(\"" + dependency + "\")");
            }
            writer.println("}");

            writer.println("""
                    task printRuntimeClasspath {
                        def runtimeClasspath = sourceSets.main.runtimeClasspath
                        inputs.files(runtimeClasspath)
                        doLast {
                            println("CLASSPATH:${runtimeClasspath.asPath}")
                        }
                    }
                    """);
        }

        File settingsFile = new File(projectDir, "settings.gradle.kts");
        try (PrintWriter writer = new PrintWriter(settingsFile)) {
            writer.println("""
                    rootProject.name = "swift-java-resolve-dependencies-temp-project"
                    """);
        }

        var connection = GradleConnector.newConnector()
                .forProjectDirectory(projectDir)
                .connect();

        try (connection) {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            PrintStream printStream = new PrintStream(outputStream);

            connection.newBuild().forTasks(":printRuntimeClasspath")
                    .setStandardError(new NoopOutputStream())
                    .setStandardOutput(printStream)
                    .run();

            var all = outputStream.toString();
            var classpath = Arrays.stream(all.split("\n"))
                    .filter(s -> s.startsWith("CLASSPATH:"))
                    .map(s -> s.substring("CLASSPATH:".length()))
                    .findFirst().orElseThrow(() -> new RuntimeException("Could not find classpath output from ':printRuntimeClasspath' task."));
            return classpath;
        }
    }

    private static class NoopOutputStream extends OutputStream {
        @Override
        public void write(int b) throws IOException {
            // ignore
        }
    }
}

which gives us:

classpath = /private/var/folders/hn/c22y4jsn4j74mw23_kpgn88w0000gn/T/java-swift-dependencies6086000906569323692/build/classes/java/main:/private/var/folders/hn/c22y4jsn4j74mw23_kpgn88w0000gn/T/java-swift-dependencies6086000906569323692/build/resources/main:/Users/ktoso/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.12.0/c6842c86792ff03b9f1d1fe2aab8dc23aa6c6f0e/commons-lang3-3.12.0.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/33.3.1-jre/852f8b363da0111e819460021ca693cacca3e8db/guava-33.3.1-jre.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-annotations/2.18.0/9bddcc56af9d90f902ef5dba7348102cd12b04e2/jackson-annotations-2.18.0.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.18.0/65e8ead7de5d8f7a53e296c363bea3182f21f925/jackson-core-2.18.0.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-databind/2.18.0/8dba1f789a75fc30b59303574fe2b269afa4d3bc/jackson-databind-2.18.0.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.google.guava/failureaccess/1.0.2/c4a06a64e650562f30b7bf9aaec1bfed43aca12b/failureaccess-1.0.2.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.google.guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/b421526c5f297295adef1c886e5246c39d4ac629/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/3.0.2/25ea2e8b0c338a877313bd4672d3fe056ea78f0d/jsr305-3.0.2.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/org.checkerframework/checker-qual/3.43.0/9425eee39e56b116d2b998b7c2cebcbd11a3c98b/checker-qual-3.43.0.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.google.errorprone/error_prone_annotations/2.28.0/59fc00087ce372de42e394d2c789295dff2d19f0/error_prone_annotations-2.28.0.jar:/Users/ktoso/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/3.0.0/7399e65dd7e9ff3404f4535b2f017093bdb134c7/j2objc-annotations-3.0.0.jar

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

No branches or pull requests

1 participant