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

Build external Java libs into standalone native libs. #431

Merged
merged 1 commit into from
Aug 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ https://github.com/j2objc-contrib/j2objc-gradle/compare/vA.B.C...vX.Y.Z
## vNext (HEAD)
Functionality:
* Automatic dependency resolution for Maven jars and Gradle projects #420
* Build external Java libraries (with source) into standalone native libraries #431
* Proper limitation of functionality on non-Mac platforms #396
* Embedded docs and versioning info for easier debugging #395

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,15 @@ import org.gradle.api.artifacts.SelfResolvingDependency

/**
* Converts `[test]compile` dependencies to their
* `j2objcTranslation` and/or `j2objcLinkage` equivalents, depending on the type
* of dependency and whether or not they are already provided in native code.
* `j2objcTranslationClosure` and/or `j2objcLinkage` equivalents, depending on the type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improve explanation:

* Converts `[test]compile` dependencies to one of the following (or their equivalent):
* j2objcLinkage - EXPLANATION HERE
* j2objcTranslationClosure - EXPLANATION HERE

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, but for DependencyConverter I just detailed how compile and testCompile get converted.
The definitions of j2objcLinkage, etc. are now in DependencyResolver's javadoc.

* of dependency and whether or not they are already provided in native code:
* <p/>
* External classfile .jar libraries you depend on via `compile` or `testCompile` will be
* converted to `j2objcTranslationClosure` dependencies of the corresponding source .jar libraries.
* Gradle projects you depend on via `compile` or `testCompile` will be converted to
* `j2objcLinkage` dependencies of the corresponding generated Objective C include headers
* and static library. See {@link DependencyResolver} for details on the differences
* between these configurations.
* <p/>
* They will be resolved to appropriate `j2objc` constructs using DependencyResolver.
*/
Expand Down Expand Up @@ -79,7 +86,7 @@ class DependencyConverter {
protected void visitSelfResolvingDependency(
SelfResolvingDependency dep) {
project.logger.debug("j2objc dependency converter: Translating file dep: $dep")
project.configurations.getByName('j2objcTranslation').dependencies.add(
project.configurations.getByName('j2objcTranslationClosure').dependencies.add(
dep.copy())
}

Expand All @@ -103,12 +110,12 @@ class DependencyConverter {
String group = dep.group == null ? '' : dep.group
String version = dep.version == null ? '' : dep.version
// TODO: Make this less fragile. What if sources don't exist for this artifact?
project.dependencies.add('j2objcTranslation', "${group}:${dep.name}:${version}:sources")
project.dependencies.add('j2objcTranslationClosure', "${group}:${dep.name}:${version}:sources")
}

protected void visitGenericDependency(Dependency dep) {
project.logger.warn("j2objc dependency converter: Unknown dependency type: $dep; copying naively")
project.configurations.getByName('j2objcTranslation').dependencies.add(
project.configurations.getByName('j2objcTranslationClosure').dependencies.add(
dep.copy())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,44 @@
*/

package com.github.j2objccontrib.j2objcgradle

import groovy.transform.CompileStatic
import groovy.transform.PackageScope
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.artifacts.SelfResolvingDependency
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginConvention
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.bundling.AbstractArchiveTask

import org.gradle.api.tasks.util.PatternSet
/**
* Resolves `j2objcTranslation` and 'j2objcLinkage' dependencies into their `j2objc` constructs.
* Resolves `j2objc*` dependencies into their `j2objc` constructs:
* <p/>
* <ul>
* <li><b>j2objcTranslationClosure</b> - The plugin will translate only the subset of
* the configuration's source jars that are actually used by this project's
* code (via --build-closure), and
* compile and link the translated code directly into this project's libraries.
* Note that if multiple projects use j2objcTranslationClosure with the same
* external library, you will likely get duplicate symbol definition errors
* when linking them together. Consider instead creating a separate Gradle
* project for that external library using j2objcTranslation.
* </li>
* <li><b>j2objcTranslation</b> - The plugin will translate the entire source jar
* provided in this configuration. Usually, this configuration is used
* to translate a single external Java library into a standalone Objective C library, that
* can then be linked (via j2objcLinkage) into your projects.
* </li>
* <li><b>j2objcLinkage</b> - The plugin will include the headers of, and link to
* the static library within, the referenced project. Usually this configuration
* is used with other projects (your own, or external libraries translated
* with j2objcTranslation) that the J2ObjC Gradle Plugin has also been applied to.
* </li>
* </ul>
*/
@PackageScope
@CompileStatic
Expand All @@ -42,20 +67,59 @@ class DependencyResolver {
}

void configureAll() {
project.configurations.getByName('j2objcTranslationClosure').each { File it ->
// These are the resolved files, NOT the dependencies themselves.
// Usually source jars.
visitTranslationClosureFile(it)
}
project.configurations.getByName('j2objcTranslation').each { File it ->
// These are the resolved files, NOT the dependencies themselves.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By resolved files, can you clarify what these files are likely to be... a jar? Or something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

visitTranslateFile(it)
// Usually source jars.
visitTranslationSourceJar(it)
}
project.configurations.getByName('j2objcLinkage').dependencies.each {
visitLink(it)
}
}

protected void visitTranslateFile(File depFile) {
protected void visitTranslationClosureFile(File depFile) {
j2objcConfig.translateSourcepaths(depFile.absolutePath)
j2objcConfig.enableBuildClosure()
}

private static final String EXTRACTION_TASK_NAME = 'j2objcTranslatedLibraryExtraction'

/**
* Adds to the main java sourceSet a to-be-generated directory that contains the contents
* of `j2objcTranslation` dependency libraries (if any).
*/
static void configureSourceSets(Project project) {
JavaPluginConvention javaConvention = project.getConvention().getPlugin(JavaPluginConvention)
SourceSet sourceSet = javaConvention.sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME)
String dir = "${project.buildDir}/translationExtraction"
sourceSet.java.srcDirs(project.file(dir))
Copy copy = project.tasks.create(EXTRACTION_TASK_NAME, Copy,
{Copy task ->
task.into(project.file(dir))
// If two libraries define the same file, fail early.
task.duplicatesStrategy = DuplicatesStrategy.FAIL
})
project.tasks.getByName(sourceSet.compileJavaTaskName).dependsOn(copy)
}

// Copy contents of sourceJarFile to build/translationExtraction
protected void visitTranslationSourceJar(File sourceJarFile) {
if (!sourceJarFile.absolutePath.endsWith('.jar')) {
String msg = "`j2objcTranslation` dependencies can only handle " +
"source jar files, not ${sourceJarFile.absolutePath}"
throw new InvalidUserDataException(msg)
}
PatternSet pattern = new PatternSet()
pattern.include('**/*.java')
Copy copy = project.tasks.getByName(EXTRACTION_TASK_NAME) as Copy
copy.from(project.zipTree(sourceJarFile).matching(pattern))
}

protected void visitLink(Dependency dep) {
if (dep instanceof ProjectDependency) {
visitLinkProjectDependency((ProjectDependency) dep)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,12 @@ class J2objcConfig {
/**
* @see #dependsOnJ2objcLib(org.gradle.api.Project)
* @deprecated Use `dependencies { j2objcLinkage project(':beforeProjectName') }` or
* `autoConfigDeps = true` instead.
* `autoConfigureDeps = true` instead.
*/
// TODO: Do this automatically based on project dependencies.
@Deprecated
void dependsOnJ2objcLib(String beforeProjectName) {
//noinspection GrDeprecatedAPIUsage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious, what was deprecated, anything we need worry about for the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is calling our own deprecated method bellow.
i.e. it is a deprecated overloading

dependsOnJ2objcLib(project.project(beforeProjectName))
}

Expand All @@ -346,10 +347,10 @@ class J2objcConfig {
* is preferable and sufficient.
*
* @deprecated Use `dependencies { j2objcLinkage project(':beforeProjectName') }` or
* `autoConfigDeps=true` instead.
* `autoConfigureDeps=true` instead.
*/
// TODO: Phase this API out, and have J2ObjC-applied project dependencies controlled
// solely via `j2objcLink` configuration.
// solely via `j2objcLinkage` configuration.
@CompileStatic(TypeCheckingMode.SKIP)
@Deprecated
void dependsOnJ2objcLib(Project beforeProject) {
Expand Down Expand Up @@ -671,6 +672,7 @@ class J2objcConfig {
void testingOnlyPrepConfigurations() {
// When testing we don't always want to apply the entire plugin
// before calling finalConfigure.
project.configurations.create('j2objcTranslationClosure')
project.configurations.create('j2objcTranslation')
project.configurations.create('j2objcLinkage')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,33 @@ class J2objcPlugin implements Plugin<Project> {
// When j2objcConfig.autoConfigureDeps is true, this configuration
// will have source paths automatically added to it. You can add
// *source* JARs/directories yourself as well.
j2objcTranslation {
j2objcTranslationClosure {
description = 'J2ObjC Java source dependencies that need to be ' +
'transitively translated via --build-closure'
'partially translated via --build-closure'
}

// If you want to translate an entire source library, regardless of which parts of the
// library code your own code depends on, use this configuration. This
// will also cause the library to be Java compiled, since you cannot translate
// a library with j2objc that does not successfully compile in Java.
// So for example:
// dependencies { j2objcTranslation 'com.google.code.gson:gson:2.3.1:sources' }
// will cause your project to produce a full gson Java classfile Jar AND a
// j2objc-translated static native library.
j2objcTranslation {
description = 'J2ObjC Java source libraries that should be fully translated ' +
'and built as stand-alone native libraries'
}

// Currently, we can only handle Project dependencies already translated to Objective-C.
j2objcLinkage {
description = 'J2ObjC native library dependencies that need to be ' +
'linked into the final library, and do not need translation'
}
}

DependencyResolver.configureSourceSets(project)

// Produces a modest amount of output
logging.captureStandardOutput LogLevel.INFO

Expand Down
36 changes: 36 additions & 0 deletions systemTests/externalLibrary1/base/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2015 the authors of j2objc-gradle (see AUTHORS file)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

apply plugin: 'java'
apply plugin: 'com.github.j2objccontrib.j2objcgradle'

repositories {
jcenter()
}

dependencies {
// Intentionally testing e2e use of a built-in j2objc library, Guava.
compile 'com.google.guava:guava:17.0'
// Normally we would reference this as compile "com.google.code.gson:gson:2.3.1"
compile project(':third_party_gson')
testCompile 'junit:junit:4.12'
}

j2objcConfig {
autoConfigureDeps true

finalConfigure()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2015 the authors of j2objc-gradle (see AUTHORS file)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example;

import com.google.common.base.Joiner;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Cube {

protected final int dimension;

public Cube(int dimension) {
this.dimension = dimension;
}

@Override
public String toString() {
return String.format("[Cube %d]", dimension);
}

public String exerciseGuava() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a separate exerciseGson method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return Joiner.on(' ').join('a', 'b', 'c');
}

public String exerciseGson() {
Gson gson = new GsonBuilder().create();
return gson.toJson(new String[]{"a", "b", "c"});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2015 the authors of j2objc-gradle (see AUTHORS file)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example;

import org.junit.Assert;
import org.junit.Test;

public class CubeTest {

@Test
public void testToString() {
Assert.assertEquals("[Cube 7]", new Cube(7).toString());
}

@Test
public void testExerciseGuava() {
Assert.assertEquals("a b c", new Cube(7).exerciseGuava());
}

@Test
public void testExerciseGson() {
Assert.assertEquals("[\"a\",\"b\",\"c\"]", new Cube(7).exerciseGson());
}
}
36 changes: 36 additions & 0 deletions systemTests/externalLibrary1/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2015 the authors of j2objc-gradle (see AUTHORS file)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

buildscript {
repositories {
jcenter()
}
dependencies {
// This is the build output of the plugin itself.
classpath fileTree(dir: '../../build/libs', include: ['*.jar'])
}
}

allprojects {
apply plugin: 'java'
test {
testLogging {
// Provide full exception info on failure, instead
// of just pointing to an HTML file.
exceptionFormat = 'full'
}
}
}
Loading