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

j2objcXcode now dependency of j2objcBuild #524

Merged
merged 1 commit into from
Oct 23, 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
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() {
Copy link
Contributor

Choose a reason for hiding this comment

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

class PodspecDetails must be modified as follows to be an input:

@EqualsAndHashCode
static class PodspecDetails implements Serializable {
...
private static final long serialVersionUID = 1L
...
}

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()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with the goal where j2objcBuild depends on j2objcXcode. But in that version, !isTaskActive is the way to bail out on this, and should not throw an error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was a little apprehensive about doing this... but going ahead and making the change. Also update the docs too.

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