Skip to content

Commit

Permalink
OS X and watchOS partial support
Browse files Browse the repository at this point in the history
- separate iOS and OS X libraries for j2objc
- separate iOS and OS X libraries built libraries
- min deployment target (default): iOS (8.3), OS X (10.8) & watchOS (2.0)
- Standardize buildType on uppercase ‘Debug’ and ‘Release’
- Link to ExceptionHandling library only on OS X

Fixes #488
  • Loading branch information
brunobowden committed Oct 13, 2015
1 parent 8c4ea6f commit b883787
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,22 @@ class J2objcConfig {
*/
String minIosVersion = '8.3'

/**
* The minimum OS X version to build against. You cannot use APIs that are not supported
* in this version.
* <p/>
* See https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html#//apple_ref/doc/uid/10000163i-CH1-SW2
*/
String minOsxVersion = '10.8'

/**
* The minimum Watch OS version to build against. You cannot use APIs that are not supported
* in this version.
* <p/>
* See https://developer.apple.com/library/ios/documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html#//apple_ref/doc/uid/10000163i-CH1-SW2
*/
String minWatchosVersion = '2.0'

// XCODE
/**
* Directory of the target Xcode project.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,15 +186,15 @@ class J2objcPlugin implements Plugin<Project> {
group 'verification'
// This transitively depends on the 'test' task from the java plugin
description 'Runs all tests in the generated Objective-C code'
buildType = 'debug'
buildType = 'Debug'
testBinaryFile = file("${buildDir}/binaries/testJ2objcExecutable/debug/testJ2objc")
}
tasks.create(name: 'j2objcTestRelease', type: TestTask,
dependsOn: ['test', 'releaseTestJ2objcExecutable']) {
group 'verification'
// This transitively depends on the 'test' task from the java plugin
description 'Runs all tests in the generated Objective-C code'
buildType = 'release'
buildType = 'Release'
testBinaryFile = file("${buildDir}/binaries/testJ2objcExecutable/release/testJ2objc")
}
tasks.create(name: 'j2objcTest', type: DefaultTask,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,12 @@ class NativeCompilation {
// https://docs.gradle.org/current/userguide/nativeBinaries.html#N161B3
task('j2objcBuildObjcDebug').configure {
dependsOn binaries.withType(NativeLibraryBinary).matching { NativeLibraryBinary lib ->
lib.buildable && lib.buildType.name == 'debug'
lib.buildable && lib.buildType.name == 'Debug'
}
}
task('j2objcBuildObjcRelease').configure {
dependsOn binaries.withType(NativeLibraryBinary).matching { NativeLibraryBinary lib ->
lib.buildable && lib.buildType.name == 'release'
lib.buildable && lib.buildType.name == 'Release'
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class PackLibrariesTask extends DefaultTask {
@TaskAction
void packLibraries() {
Utils.requireMacOSX('j2objcPackLibraries task')
assert buildType in ['Debug', 'Release']

Utils.projectDelete(project, getOutputLibDirFile())
getOutputLibDirFile().mkdirs()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import groovy.transform.CompileStatic
import org.gradle.api.DefaultTask
import org.gradle.api.InvalidUserDataException
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction

Expand Down Expand Up @@ -89,15 +88,24 @@ class PodspecTask extends DefaultTask {

// podspec creation
// TODO: allow custom list of libraries
String libDirDebug = new File(getDestLibDirFile(), '/iosDebug').absolutePath
String libDirRelease = new File(getDestLibDirFile(), '/iosRelease').absolutePath
// iOS packed libraries are shared with watchOS
String libDirIosDebug = new File(getDestLibDirFile(), '/iosDebug').absolutePath
String libDirIosRelease = new File(getDestLibDirFile(), '/iosRelease').absolutePath
String libDirOsxDebug = new File(getDestLibDirFile(), '/x86_64Debug').absolutePath
String libDirOsxRelease = new File(getDestLibDirFile(), '/x86_64Release').absolutePath

J2objcConfig j2objcConfig = J2objcConfig.from(project)

String podspecContentsDebug =
genPodspec(getPodNameDebug(), libDirDebug, libName, getJ2objcHome(),
headerIncludePath, resourceIncludePath)
genPodspec(getPodNameDebug(), headerIncludePath, resourceIncludePath,
libName, getJ2objcHome(),
libDirIosDebug, libDirOsxDebug, libDirIosDebug,
j2objcConfig.minIosVersion, j2objcConfig.minOsxVersion, j2objcConfig.minWatchosVersion)
String podspecContentsRelease =
genPodspec(getPodNameRelease(), libDirRelease, libName, getJ2objcHome(),
headerIncludePath, resourceIncludePath)
genPodspec(getPodNameRelease(), headerIncludePath, resourceIncludePath,
libName, getJ2objcHome(),
libDirIosRelease, libDirOsxRelease, libDirIosRelease,
j2objcConfig.minIosVersion, j2objcConfig.minOsxVersion, j2objcConfig.minWatchosVersion)

logger.debug("Writing debug podspec... ${getPodspecDebug()}")
getPodspecDebug().write(podspecContentsDebug)
Expand All @@ -107,22 +115,29 @@ class PodspecTask extends DefaultTask {

// Podspec references are relative to project.buildDir
@VisibleForTesting
static String genPodspec(String podname, String libDir, String libName, String j2objcHome,
String publicHeadersDir, String resourceDir) {
static String genPodspec(String podname, String publicHeadersDir, String resourceDir,
String libName, String j2objcHome,
String libDirIos, String libDirOsx, String libDirWatchos,
String minIos, String minOsx, String minWatchos) {

// Absolute paths for Xcode command line
validatePodspecPath(libDir, false)
validatePodspecPath(libDirIos, false)
validatePodspecPath(libDirOsx, false)
validatePodspecPath(j2objcHome, false)

// Relative paths for content referenced by CocoaPods
validatePodspecPath(publicHeadersDir, false)
validatePodspecPath(resourceDir, true)

Utils.validateVersion(minIos, 'minIosVersion')
Utils.validateVersion(minOsx, 'minOsxVersion')
Utils.validateVersion(minWatchos, 'minWatchosVersion')

// TODO: CocoaPods strongly recommends switching from 'resources' to 'resource_bundles'
// http://guides.cocoapods.org/syntax/podspec.html#resource_bundles

// TODO: Distinguish ios to OS X builds:
// https://github.com/j2objc-contrib/j2objc-gradle/issues/488
// TODO: replace xcconfig with {pod|user}_target_xcconfig
// See 'Split of xcconfig' from: http://blog.cocoapods.org/CocoaPods-0.38/

// File and line separators assumed to be '/' and '\n' as podspec can only be used on OS X
return "Pod::Spec.new do |spec|\n" +
Expand All @@ -134,9 +149,22 @@ class PodspecTask extends DefaultTask {
" spec.libraries = " + // continuation of same line
"'ObjC', 'guava', 'javax_inject', 'jre_emul', 'jsr305', 'z', 'icucore', '$libName'\n" +
" spec.xcconfig = {\n" +
" 'HEADER_SEARCH_PATHS' => '$j2objcHome/include $publicHeadersDir',\n" +
" 'LIBRARY_SEARCH_PATHS' => '$j2objcHome/lib $libDir'\n" +
" 'HEADER_SEARCH_PATHS' => '$j2objcHome/include $publicHeadersDir'\n" +
" }\n" +
" spec.ios.xcconfig = {\n" +
" 'LIBRARY_SEARCH_PATHS' => '$j2objcHome/lib $libDirIos'\n" +
" }\n" +
" spec.osx.xcconfig = {\n" +
" 'LIBRARY_SEARCH_PATHS' => '$j2objcHome/lib/macosx $libDirOsx'\n" +
" }\n" +
" spec.watchos.xcconfig = {\n" +
" 'LIBRARY_SEARCH_PATHS' => '$j2objcHome/lib $libDirWatchos'\n" +
" }\n" +
// http://guides.cocoapods.org/syntax/podspec.html#deployment_target
" spec.ios.deployment_target = '$minIos'\n" +
" spec.osx.deployment_target = '$minOsx'\n" +
" spec.watchos.deployment_target = '$minWatchos'\n" +
" spec.osx.frameworks = 'ExceptionHandling'\n" +
"end\n"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class TestTask extends DefaultTask {
@InputFile
File testBinaryFile

// 'debug' or 'release'
// 'Debug' or 'Release'
@Input
String buildType

Expand Down Expand Up @@ -88,7 +88,7 @@ class TestTask extends DefaultTask {
@OutputDirectory
// Combines main/test resources and test executables
File getJ2objcTestDirFile() {
assert buildType in ['debug', 'release']
assert buildType in ['Debug', 'Release']
return new File(project.buildDir, "j2objcTest/$buildType")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ class Utils {
}
}

static void validateVersion(String version, String type) {
// Requires at least a major and minor version number
Matcher versionMatcher = (version =~ /^[0-9]*(\.[0-9]+)+$/)
if (!versionMatcher.find()) {
throw new InvalidUserDataException("Invalid version for $type: $version")
}
}

private static String fakeOSName = ''

/* This allows faking of is(Linux|Windows|MacOSX) methods but misses java.io.File separators.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,21 @@ class PodspecTaskTest {
" spec.requires_arc = true",
" spec.libraries = 'ObjC', 'guava', 'javax_inject', 'jre_emul', 'jsr305', 'z', 'icucore', '$libName'",
" spec.xcconfig = {",
" 'HEADER_SEARCH_PATHS' => '${j2objcHome}/include ${proj.file('build/j2objcOutputs/src/main/objc')}',",
" 'HEADER_SEARCH_PATHS' => '${j2objcHome}/include ${proj.file('build/j2objcOutputs/src/main/objc')}'",
" }",
" spec.ios.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib ${proj.file('build/j2objcOutputs/lib/iosDebug').absolutePath}'",
" }",
" spec.osx.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib/macosx ${proj.file('build/j2objcOutputs/lib/x86_64Debug').absolutePath}'",
" }",
" spec.watchos.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib ${proj.file('build/j2objcOutputs/lib/iosDebug').absolutePath}'",
" }",
" spec.ios.deployment_target = '8.3'",
" spec.osx.deployment_target = '10.8'",
" spec.watchos.deployment_target = '2.0'",
" spec.osx.frameworks = 'ExceptionHandling'",
"end"]
File podspecDebug = proj.file("build/${podNameDebug}.podspec")
List<String> readPodspecDebug = podspecDebug.readLines()
Expand All @@ -93,9 +105,21 @@ class PodspecTaskTest {
" spec.requires_arc = true",
" spec.libraries = 'ObjC', 'guava', 'javax_inject', 'jre_emul', 'jsr305', 'z', 'icucore', '$libName'",
" spec.xcconfig = {",
" 'HEADER_SEARCH_PATHS' => '${j2objcHome}/include ${proj.file('build/j2objcOutputs/src/main/objc')}',",
" 'HEADER_SEARCH_PATHS' => '${j2objcHome}/include ${proj.file('build/j2objcOutputs/src/main/objc')}'",
" }",
" spec.ios.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib ${proj.file('build/j2objcOutputs/lib/iosRelease').absolutePath}'",
" }",
" spec.osx.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib/macosx ${proj.file('build/j2objcOutputs/lib/x86_64Release').absolutePath}'",
" }",
" spec.watchos.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '${j2objcHome}/lib ${proj.file('build/j2objcOutputs/lib/iosRelease').absolutePath}'",
" }",
" spec.ios.deployment_target = '8.3'",
" spec.osx.deployment_target = '10.8'",
" spec.watchos.deployment_target = '2.0'",
" spec.osx.frameworks = 'ExceptionHandling'",
"end"]
File podspecRelease = proj.file("build/${podNameRelease}.podspec")
List<String> readPodspecRelease = podspecRelease.readLines()
Expand All @@ -105,8 +129,11 @@ class PodspecTaskTest {
@Test
void testGenPodspec() {
List<String> podspecDebug = PodspecTask.genPodspec(
'POD-NAME', '/DEST-LIB-DIR', 'LIB-NAME',
'/J2OBJC_HOME', '/HEADER_INCLUDE', 'MAIN-RESOURCES').split('\n')
'POD-NAME', '/HEADER_INCLUDE', 'MAIN-RESOURCES',
'LIB-NAME', '/J2OBJC_HOME',
'/LIB-DIR-IOS', '/LIB-DIR-OSX', '/LIB-DIR-WATCHOS',
// Using non-existent OS version numbers to ensure that no defaults are being used
'8.3.1', '10.8.1', '2.0.1').split('\n')

List<String> expectedPodspecDebug = [
"Pod::Spec.new do |spec|",
Expand All @@ -117,9 +144,21 @@ class PodspecTaskTest {
" spec.requires_arc = true",
" spec.libraries = 'ObjC', 'guava', 'javax_inject', 'jre_emul', 'jsr305', 'z', 'icucore', 'LIB-NAME'",
" spec.xcconfig = {",
" 'HEADER_SEARCH_PATHS' => '/J2OBJC_HOME/include /HEADER_INCLUDE',",
" 'LIBRARY_SEARCH_PATHS' => '/J2OBJC_HOME/lib /DEST-LIB-DIR'",
" 'HEADER_SEARCH_PATHS' => '/J2OBJC_HOME/include /HEADER_INCLUDE'",
" }",
" spec.ios.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '/J2OBJC_HOME/lib /LIB-DIR-IOS'",
" }",
" spec.osx.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '/J2OBJC_HOME/lib/macosx /LIB-DIR-OSX'",
" }",
" spec.watchos.xcconfig = {",
" 'LIBRARY_SEARCH_PATHS' => '/J2OBJC_HOME/lib /LIB-DIR-WATCHOS'",
" }",
" spec.ios.deployment_target = '8.3.1'",
" spec.osx.deployment_target = '10.8.1'",
" spec.watchos.deployment_target = '2.0.1'",
" spec.osx.frameworks = 'ExceptionHandling'",
"end"]

assert expectedPodspecDebug == podspecDebug
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ class TestTaskTest {

j2objcTest = (TestTask) proj.tasks.create(name: 'j2objcTest', type: TestTask) {
testBinaryFile = proj.file(proj.file('build/binaries/testJ2objcExecutable/debug/testJ2objc'))
buildType = 'debug'
buildType = 'Debug'
}
}

Expand All @@ -167,7 +167,7 @@ class TestTaskTest {
mockProjectExec.demandExecAndReturn(
null,
[
proj.file('build/j2objcTest/debug/testJ2objc').absolutePath,
proj.file('build/j2objcTest/Debug/testJ2objc').absolutePath,
"org.junit.runner.JUnitCore",
],
'OK (0 test)', // NOTE: 'test' is singular for stdout
Expand All @@ -194,7 +194,7 @@ class TestTaskTest {
mockProjectExec.demandExecAndReturn(
null,
[
proj.file('build/j2objcTest/debug/testJ2objc').absolutePath,
proj.file('build/j2objcTest/Debug/testJ2objc').absolutePath,
"org.junit.runner.JUnitCore",
],
'OK (0 test)', // NOTE: 'test' is singular for stdout
Expand All @@ -215,7 +215,7 @@ class TestTaskTest {
mockProjectExec.demandExecAndReturn(
null,
[
proj.file('build/j2objcTest/debug/testJ2objc').absolutePath,
proj.file('build/j2objcTest/Debug/testJ2objc').absolutePath,
"org.junit.runner.JUnitCore",
],
'OK (1 test)', // NOTE: 'test' is singular for stdout
Expand All @@ -236,7 +236,7 @@ class TestTaskTest {
mockProjectExec.demandExecAndReturn(
null,
[
proj.file('build/j2objcTest/debug/testJ2objc').absolutePath,
proj.file('build/j2objcTest/Debug/testJ2objc').absolutePath,
"org.junit.runner.JUnitCore",
],
'IGNORE\nOK (2 tests)\nIGNORE', // stdout
Expand All @@ -257,7 +257,7 @@ class TestTaskTest {
mockProjectExec.demandExecAndReturn(
null,
[
proj.file('build/j2objcTest/debug/testJ2objc').absolutePath,
proj.file('build/j2objcTest/Debug/testJ2objc').absolutePath,
"org.junit.runner.JUnitCore",
],
'OK (2 testXXXX)', // NOTE: invalid stdout fails matchRegexOutputs
Expand All @@ -272,16 +272,16 @@ class TestTaskTest {
private void demandCopyForJ2objcTest(MockProjectExec mockProjectExec) {
// Delete test directory
mockProjectExec.demandDeleteAndReturn(
proj.file('build/j2objcTest/debug').absolutePath)
proj.file('build/j2objcTest/Debug').absolutePath)
// Copy main resources, test resources and test binary to test directory
mockProjectExec.demandMkDirAndReturn(
proj.file('build/j2objcTest/debug').absolutePath)
proj.file('build/j2objcTest/Debug').absolutePath)
mockProjectExec.demandCopyAndReturn(
proj.file('build/j2objcTest/debug').absolutePath,
proj.file('build/j2objcTest/Debug').absolutePath,
proj.file('src/main/resources').absolutePath,
proj.file('src/test/resources').absolutePath)
mockProjectExec.demandCopyAndReturn(
proj.file('build/j2objcTest/debug').absolutePath,
proj.file('build/j2objcTest/Debug').absolutePath,
proj.file('build/binaries/testJ2objcExecutable/debug/testJ2objc').absolutePath)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ class UtilsTest {
Utils.checkMinGradleVersion(GradleVersion.version('2.3'))
}

@Test
void testValidateVersion() {
Utils.validateVersion('0.0', 'test')
Utils.validateVersion('0.38', 'test')
Utils.validateVersion('2.0', 'test')
Utils.validateVersion('8.3', 'test')
Utils.validateVersion('10.8', 'test')
}

@Test(expected=InvalidUserDataException)
void testValidateVersion_doublePeriod() {
Utils.validateVersion('10..8', 'test')
}

@Test(expected=InvalidUserDataException)
void testValidateVersion_string() {
Utils.validateVersion('1.a', 'test')
}

@Test
void testGetLowerCaseOSName() {
// Redundant method call but included for clarity
Expand Down
8 changes: 4 additions & 4 deletions systemTests/multiProject1/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ EXTERNAL SOURCES:
:path: ../extended/build

SPEC CHECKSUMS:
j2objc-base-debug: d6324af485dfb6c80ea99bf2efb69ae70966b10e
j2objc-base-release: d769f0c555c8c5d1be0f775920c51f01c74f23dd
j2objc-extended-debug: 6ea07587317140c13f85c24e8aa172349174f640
j2objc-extended-release: 24d25ad17f44675a51cd0609dc5b996dbd2981b8
j2objc-base-debug: 478bb2f19db54654adc8cc3e606914adaacaef5c
j2objc-base-release: 65381df5900d9be5b05afd7d41580d1e98ce1dee
j2objc-extended-debug: ea5b498adb5bfe52c3126b90d16a1dfcbc9f2af4
j2objc-extended-release: 9c2696a59c8e50aeed6d40bb661574cd1b833c52

COCOAPODS: 0.38.2

0 comments on commit b883787

Please sign in to comment.