Skip to content

Commit

Permalink
Merge pull request #524 from brunobowden/improve
Browse files Browse the repository at this point in the history
j2objcXcode now dependency of j2objcBuild
  • Loading branch information
brunobowden committed Oct 23, 2015
2 parents 94223a8 + e33e86e commit f6def79
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 117 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ before_install:
# Don't spew graphic art.
- export TERM=dumb
- env
# Cocoapods must be manually due to issues with Travis:
# http://docs.travis-ci.com/user/common-build-problems/#Mac%3A-Errors-running-CocoaPods
- if [ "$USING_OS" = "osx" ]; then gem install cocoapods -v '0.39.0'; fi
# Travis doesn't let us select the JDK version on OS X. Force it ourselves.
- if [ "$USING_OS" = "osx" ]; then ./install-osx-jdk7.sh; fi

Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ New functionality:
* Translate and run standalone test source Jar files (such as external library dependencies) #489
* Test-only dependencies on other libraries and projects #489
* Cocoa Pods supports multi-project applications #504
* iOS, watchOS, and OS X applications can be setup using Cocoa Pods #506
* iOS and OS X applications can be setup using Cocoa Pods #506
* Validate version of j2objc and provide install instructions #515

Breaking changes/functionality:
Expand Down
15 changes: 4 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ plugins {
// Plugin settings:
j2objcConfig {
// Xcode project directory (suggested directory name)
xcodeProjectDir '../ios'
xcodeProjectDir '../ios' // suggested directory name
xcodeTargetsIos 'IOS-APP', 'IOS-APPTests' // replace with your iOS targets
finalConfigure() // Must be last call to configuration
}
```

Info on additional `j2objcConfig` settings are in
[J2objcConfig.groovy](https://github.com/j2objc-contrib/j2objc-gradle/blob/master/src/main/groovy/com/github/j2objccontrib/j2objcgradle/J2objcConfig.groovy#L30).
The default will link the transpiled code in to all of your Xcode build targets. To specify
a subset, add a line for `xcodeTargets 'IOS-APP', 'IOS-APP-TESTS', 'WATCHKIT-APP', ...`.
The Xcode targets may also be set for xcodeTargetsOsx and xcodeTargetsWatchos
([issue #525 to get watchOS working](https://github.com/j2objc-contrib/j2objc-gradle/issues/525)).
If your `shared` project depends on any other projects or third-party libraries, you may
need to [add them manually](FAQ.md#how-do-i-setup-dependencies-with-j2objc) if they aren't
[linked by default](FAQ.md#what-libraries-are-linked-by-default).
Expand Down Expand Up @@ -98,13 +98,6 @@ be run as follows:

./gradlew shared:build

During development, to build the libraries and update Xcode (skipping the tests):

./gradlew shared:j2objcXcode

For a complete build, run both:

./gradlew shared:build shared:j2objcXcode

### Problems

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import org.gradle.util.GradleVersion
* Main plugin class for creation of extension object and all the tasks.
*/
class J2objcPlugin implements Plugin<Project> {

@Override
void apply(Project project) {
String version = BuildInfo.VERSION
Expand Down Expand Up @@ -195,7 +195,7 @@ class J2objcPlugin implements Plugin<Project> {
// If both release and debug tests would run, run the debug tests first - ideally
// the failure messages will be easier to understand (ex. Java line numbers).
lateShouldRunAfter(project, 'j2objcTestRelease', 'j2objcTestDebug')

tasks.create(name: 'j2objcTest', type: DefaultTask,
dependsOn: ['j2objcCycleFinder', 'j2objcTestDebug', 'j2objcTestRelease']) {
group 'build'
Expand All @@ -219,15 +219,9 @@ class J2objcPlugin implements Plugin<Project> {
buildType = 'Release'
}

// Assemble
tasks.create(name: 'j2objcPodspec', type: PodspecTask,
// Podspec may reference sources and resources that haven't yet been built
dependsOn: ['j2objcPreBuild']) {
group 'build'
description 'Generate debug and release podspec that may be used for Xcode'
}
// Assemble files
tasks.create(name: 'j2objcAssembleResources', type: AssembleResourcesTask,
dependsOn: ['j2objcPreBuild', 'j2objcPodspec']) {
dependsOn: ['j2objcPreBuild']) {
group 'build'
description 'Copies mains and test resources to assembly directories'
}
Expand All @@ -238,6 +232,7 @@ class J2objcPlugin implements Plugin<Project> {
srcGenMainDir = j2objcSrcGenMainDir
srcGenTestDir = j2objcSrcGenTestDir
}
// Assemble libaries
tasks.create(name: 'j2objcAssembleDebug', type: AssembleLibrariesTask,
dependsOn: ['j2objcPackLibrariesDebug', 'j2objcAssembleSource', 'j2objcAssembleResources']) {
group 'build'
Expand All @@ -254,8 +249,21 @@ class J2objcPlugin implements Plugin<Project> {
srcLibDir = file("${buildDir}/binaries/${project.name}-j2objcStaticLibrary")
srcPackedLibDir = file("${buildDir}/packedBinaries/${project.name}-j2objcStaticLibrary")
}
// Assemble podspec and update Xcode
tasks.create(name: 'j2objcPodspec', type: PodspecTask,
// Podspec may reference sources and resources that haven't yet been built
dependsOn: ['j2objcPreBuild']) {
group 'build'
description 'Generate debug and release podspec that may be used for Xcode'
}
tasks.create(name: 'j2objcXcode', type: XcodeTask,
dependsOn: 'j2objcPodspec') {
group 'build'
description 'Depends on j2objc translation, create a Pod file link it to Xcode project'
}
// Assemble final task
tasks.create(name: 'j2objcAssemble', type: DefaultTask,
dependsOn: ['j2objcAssembleDebug', 'j2objcAssembleRelease']) {
dependsOn: ['j2objcAssembleDebug', 'j2objcAssembleRelease', 'j2objcXcode']) {
group 'build'
description "Marker task for all assembly tasks that take part in regular j2objc builds"
}
Expand All @@ -280,13 +288,6 @@ class J2objcPlugin implements Plugin<Project> {
description "Marker task for all tasks that take part in regular j2objc builds"
}
lateDependsOn(project, 'build', 'j2objcBuild')

// TODO: Where shall we fit this task in the plugin lifecycle?
tasks.create(name: 'j2objcXcode', type: XcodeTask,
dependsOn: 'j2objcAssemble') {
// This is not in the build group because you do not need to do it on every build.
description 'Depends on j2objc translation, create a Pod file link it to Xcode project'
}
}
}

Expand All @@ -311,7 +312,7 @@ class J2objcPlugin implements Plugin<Project> {
}
}
}

// Causes afterTask to run after beforeTaskName iff:
// 1) Both afterTask and beforeTask would be run anyway (does not cause beforeTask to
// run, unlike 'dependsOn').
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.github.j2objccontrib.j2objcgradle.tasks
import com.github.j2objccontrib.j2objcgradle.J2objcConfig
import com.google.common.annotations.VisibleForTesting
import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import org.gradle.api.DefaultTask
import org.gradle.api.InvalidUserDataException
import org.gradle.api.Project
Expand Down Expand Up @@ -53,6 +54,18 @@ class XcodeTask extends DefaultTask {
@Input @Optional
String getXcodeProjectDir() { return J2objcConfig.from(project).xcodeProjectDir }

boolean isTaskActive() { return getXcodeProjectDir() != null }

@Input
// List of all dependencies
List<PodspecDetails> getPodspecDependencies() {
if (!isTaskActive()) {
// Optimization for only calculating dependencies where needed
return []
}
return getPodspecDependencies(getProject(), new HashSet<Project>())
}

@Input
List<String> getXcodeTargetsIos() { return J2objcConfig.from(project).xcodeTargetsIos }
@Input
Expand All @@ -69,11 +82,15 @@ class XcodeTask extends DefaultTask {

@OutputFile
File getPodfileFile() {
verifyXcodeArgs()
return project.file(new File(getXcodeProjectDir(), '/Podfile'))
}

static class PodspecDetails {
@EqualsAndHashCode
// Must be serializable to be used as an @Input
static class PodspecDetails implements Serializable {
// Increment this when the serialization output changes
private static final long serialVersionUID = 1L;

String projectName
File podspecDebug
File podspecRelease
Expand All @@ -87,6 +104,16 @@ class XcodeTask extends DefaultTask {
String getPodMethodName() {
return "j2objc_$projectName"
}

@SuppressWarnings('unused')
private static void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
}

@SuppressWarnings('unused')
private static void readObject(ObjectInputStream s) throws IOException {
s.defaultReadObject();
}
}

static class XcodeTargetDetails {
Expand Down Expand Up @@ -114,7 +141,25 @@ class XcodeTask extends DefaultTask {
void xcodeConfig() {
Utils.requireMacOSX('j2objcXcode task')

verifyXcodeArgs()
if (!isTaskActive()) {
logger.debug("j2objcXcode task disabled for ${project.name}")
return
}

// // TODO: figure out how to display error when not configured on root project
// String message =
// "xcodeProjectDir need to be configured in ${project.name}'s build.gradle.\n" +
// "The directory should point to the location containing your Xcode project,\n" +
// "including the IOS-APP.xccodeproj file.\n" +
// "\n" +
// "j2objcConfig {\n" +
// " xcodeProjectDir '../ios'\n" +
// "}\n" +
// "\n" +
// "Alternatively disable the j2objcXcode task if you wish to do your own Xcode build.\n"
// "Also see the guidelines for the folder structure:\n" +
// "https://github.com/j2objc-contrib/j2objc-gradle/blob/master/FAQ.md#what-is-the-recommended-folder-structure-for-my-app\n"
// throw new InvalidUserDataException(message)

// link the podspec in pod file
File podfile = getPodfileFile()
Expand All @@ -129,8 +174,8 @@ class XcodeTask extends DefaultTask {
"To fix this:\n" +
"\n" +
"1) Set xcodeProjectDir to the directory containing 'IOS-APP.xcodeproj':\n" +
" current value from j2objcConfig: ${getXcodeProjectDir()}\n" +
" current value for absolute path: $xcodeAbsPath\n" +
" curent value: ${getXcodeProjectDir()}\n" +
" resolves to: $xcodeAbsPath\n" +
"\n" +
"2) Within that directory, create the Podfile with:\n" +
" (cd $xcodeAbsPath && pod init)\n" +
Expand All @@ -142,8 +187,7 @@ class XcodeTask extends DefaultTask {
logger.debug("Pod exists at path: ${getXcodeProjectDir()}")

// Write Podfile based on all the podspecs from dependent projects
List<PodspecDetails> podspecDetailsList =
getPodspecsFromProject(getProject(), new HashSet<Project>())
List<PodspecDetails> podspecDetailsList = getPodspecDependencies()

XcodeTargetDetails xcodeTargetDetails = new XcodeTargetDetails(
getXcodeTargetsIos(), getXcodeTargetsOsx(), getXcodeTargetsWatchos(),
Expand Down Expand Up @@ -196,7 +240,8 @@ class XcodeTask extends DefaultTask {
* @return List of Files corresponding to debug / release pair of podspecs
* Even entries in the list are debug podspecs, odd for release podspecs
*/
private List<PodspecDetails> getPodspecsFromProject(Project proj, Set<Project> visitedProjects) {
@VisibleForTesting
List<PodspecDetails> getPodspecDependencies(Project proj, Set<Project> visitedProjects) {

// Find podspecs generated by this project
List<PodspecDetails> podspecs = new ArrayList<>()
Expand All @@ -211,30 +256,12 @@ class XcodeTask extends DefaultTask {

J2objcConfig j2objcConfig = proj.getExtensions().getByType(J2objcConfig)
j2objcConfig.getBeforeProjects().each { Project beforeProject ->
podspecs.addAll(getPodspecsFromProject(beforeProject, visitedProjects))
podspecs.addAll(getPodspecDependencies(beforeProject, visitedProjects))
}

return podspecs
}

@VisibleForTesting
void verifyXcodeArgs() {
if (getXcodeProjectDir() == null) {
String message =
"xcodeProjectDir need to be configured in ${project.name}'s build.gradle.\n" +
"The directory should point to the location containing your Xcode project,\n" +
"including the IOS-APP.xccodeproj file.\n" +
"\n" +
"j2objcConfig {\n" +
" xcodeProjectDir '../ios'\n" +
"}\n" +
"\n" +
"Also see the guidelines for the folder structure:\n" +
"https://github.com/j2objc-contrib/j2objc-gradle/blob/master/FAQ.md#what-is-the-recommended-folder-structure-for-my-app"
throw new InvalidUserDataException(message)
}
}

/**
* Extracts xcode targets in Podfile.
*/
Expand Down Expand Up @@ -355,7 +382,7 @@ class XcodeTask extends DefaultTask {
List<String> newPodfileLines = new ArrayList<String>(oldPodfileLines)

newPodfileLines = updatePodfile(
newPodfileLines, podspecDetailsList, xcodeTargetDetails, podfile, logger)
newPodfileLines, podspecDetailsList, xcodeTargetDetails, podfile)

// Write file only if it's changed
if (!oldPodfileLines.equals(newPodfileLines)) {
Expand All @@ -368,7 +395,7 @@ class XcodeTask extends DefaultTask {
List<String> podfileLines,
List<PodspecDetails> podspecDetailsList,
XcodeTargetDetails xcodeTargetDetails,
File podfile, Logger logger) {
File podfile) {

List<String> podfileTargets = extractXcodeTargets(podfileLines)
verifyTargets(xcodeTargetDetails.xcodeTargetsIos, podfileTargets, 'xcodeTargetsIos')
Expand All @@ -378,7 +405,7 @@ class XcodeTask extends DefaultTask {
if (xcodeTargetDetails.xcodeTargetsIos.isEmpty() &&
xcodeTargetDetails.xcodeTargetsOsx.isEmpty() &&
xcodeTargetDetails.xcodeTargetsWatchos.isEmpty()) {
// Need to warn about configuring
// Give example for configuring iOS as that's the common case
throw new InvalidUserDataException(
"You must configure the xcode targets for the J2ObjC Gradle Plugin.\n" +
"It must be a subset of the valid targets: '${podfileTargets.join("', '")}'\n" +
Expand All @@ -393,9 +420,8 @@ class XcodeTask extends DefaultTask {
// update pod methods
List<String> newPodfileLines = updatePodMethods(podfileLines, podspecDetailsList, podfile)

// Iterate over all podfileTargets as some may need to be cleared
newPodfileLines = updatePodfileTargets(
newPodfileLines, podspecDetailsList, xcodeTargetDetails)
// update pod targets
newPodfileLines = updatePodfileTargets(newPodfileLines, podspecDetailsList, xcodeTargetDetails)

return newPodfileLines
}
Expand Down
Loading

0 comments on commit f6def79

Please sign in to comment.