diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy index b8b548d7..825ed9c4 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy @@ -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. + *

+ * 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. + *

+ * 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. diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcPlugin.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcPlugin.groovy index 771aeb91..a1fa7a05 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcPlugin.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcPlugin.groovy @@ -186,7 +186,7 @@ class J2objcPlugin implements Plugin { 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, @@ -194,7 +194,7 @@ class J2objcPlugin implements Plugin { 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, diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/NativeCompilation.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/NativeCompilation.groovy index 5ae9f452..9ddb54d7 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/NativeCompilation.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/NativeCompilation.groovy @@ -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' } } } diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTask.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTask.groovy index 8f1a7864..0e8fda7c 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTask.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PackLibrariesTask.groovy @@ -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() diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PodspecTask.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PodspecTask.groovy index b4b052fa..97594f95 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PodspecTask.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PodspecTask.groovy @@ -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 @@ -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) @@ -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" + @@ -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" } diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTask.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTask.groovy index 2aac3f1b..b9b58bd0 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTask.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTask.groovy @@ -43,7 +43,7 @@ class TestTask extends DefaultTask { @InputFile File testBinaryFile - // 'debug' or 'release' + // 'Debug' or 'Release' @Input String buildType @@ -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") } diff --git a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/Utils.groovy b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/Utils.groovy index 24969880..2838e7f9 100644 --- a/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/Utils.groovy +++ b/src/main/groovy/com/github/j2objccontrib/j2objcgradle/tasks/Utils.groovy @@ -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. diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PodspecTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PodspecTaskTest.groovy index 5b3d9c51..9302dabf 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PodspecTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/PodspecTaskTest.groovy @@ -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 readPodspecDebug = podspecDebug.readLines() @@ -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 readPodspecRelease = podspecRelease.readLines() @@ -105,8 +129,11 @@ class PodspecTaskTest { @Test void testGenPodspec() { List 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 expectedPodspecDebug = [ "Pod::Spec.new do |spec|", @@ -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 diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTaskTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTaskTest.groovy index fa2922d6..9fff9de8 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTaskTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/TestTaskTest.groovy @@ -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' } } @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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) } diff --git a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/UtilsTest.groovy b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/UtilsTest.groovy index f333760a..cf50bb31 100644 --- a/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/UtilsTest.groovy +++ b/src/test/groovy/com/github/j2objccontrib/j2objcgradle/tasks/UtilsTest.groovy @@ -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 diff --git a/systemTests/multiProject1/ios/Podfile.lock b/systemTests/multiProject1/ios/Podfile.lock index 703475d0..714ab8b3 100644 --- a/systemTests/multiProject1/ios/Podfile.lock +++ b/systemTests/multiProject1/ios/Podfile.lock @@ -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