org.openbakery.XcodeBuildPluginExtension.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xcode-plugin Show documentation
Show all versions of xcode-plugin Show documentation
XCode-Plugin is a plugin to allow custom XCode projects to build as generated by CMake
/*
* Copyright 2013 the original author or authors.
*
* 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 org.openbakery
import org.apache.commons.io.filefilter.SuffixFileFilter
import org.apache.commons.lang.StringUtils
import org.gradle.api.Project
import org.gradle.util.ConfigureUtil
import org.openbakery.signing.Signing
import org.openbakery.xcode.Destination
import org.openbakery.xcode.Devices
import org.openbakery.xcode.Type
import org.openbakery.xcode.Xcode
import org.openbakery.xcode.XcodebuildParameters
import org.openbakery.util.PlistHelper
import org.openbakery.util.VariableResolver
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import static groovy.io.FileType.*
import static groovy.io.FileVisitResult.*
/*
^
should be migrated to this -> and renamed to Device
enum Devices {
PHONE(1<<0),
PAD(1<<1),
WATCH(1<<2),
TV(1<<3)
private final int value;
Devices(int value) {
this.value = value
}
public int getValue() {
return value
}
public boolean is(Devices device) {
return (this.value & device.value) > 0
}
}
*/
class XcodeBuildPluginExtension {
public final static KEYCHAIN_NAME_BASE = "gradle-"
private static Logger logger = LoggerFactory.getLogger(XcodeBuildPluginExtension.class)
XcodebuildParameters _parameters = new XcodebuildParameters()
String infoPlist = null
String scheme = null
String configuration = 'Debug'
boolean simulator = true
Type type = Type.iOS
String target = null
Object dstRoot
Object objRoot
Object symRoot
Object sharedPrecompsDir
Object derivedDataPath
Signing signing = null
def additionalParameters = null
String bundleNameSuffix = null
List arch = null
String workspace = null
String xcodeVersion = null
Map environment = null
String productName = null
String bundleName = null
String productType = "app"
String ipaFileName = null
File projectFile
Boolean bitcode = false
boolean useXcodebuildArchive = false
Devices devices = Devices.UNIVERSAL
CommandRunner commandRunner
VariableResolver variableResolver
PlistHelper plistHelper
Xcode xcode
HashMap projectSettings = new HashMap<>()
/**
* internal parameters
*/
private final Project project
public XcodeBuildPluginExtension(Project project) {
this.project = project;
this.signing = new Signing(project)
this.variableResolver = new VariableResolver(project)
commandRunner = new CommandRunner()
plistHelper = new PlistHelper(commandRunner)
this.dstRoot = {
return project.getFileResolver().withBaseDir(project.getBuildDir()).resolve("dst")
}
this.objRoot = {
return project.getFileResolver().withBaseDir(project.getBuildDir()).resolve("obj")
}
this.symRoot = {
return project.getFileResolver().withBaseDir(project.getBuildDir()).resolve("sym")
}
this.sharedPrecompsDir = {
return project.getFileResolver().withBaseDir(project.getBuildDir()).resolve("shared")
}
this.derivedDataPath = {
return project.getFileResolver().withBaseDir(project.getBuildDir()).resolve("derivedData")
}
}
String getWorkspace() {
if (workspace != null) {
return workspace
}
String[] fileList = project.projectDir.list(new SuffixFileFilter(".xcworkspace"))
if (fileList != null && fileList.length) {
return fileList[0]
}
return null
}
void setDerivedDataPath(File derivedDataPath) {
this.derivedDataPath = derivedDataPath
}
void setDstRoot(File dstRoot) {
this.dstRoot = dstRoot
}
void setObjRoot(File objRoot) {
this.objRoot = objRoot
}
void setSymRoot(File symRoot) {
this.symRoot = symRoot
}
void setSharedPrecompsDir(File sharedPrecompsDir) {
this.sharedPrecompsDir = sharedPrecompsDir
}
File getDstRoot() {
if (dstRoot instanceof File) {
return dstRoot
}
return project.file(dstRoot)
}
File getObjRoot() {
if (objRoot instanceof File) {
return objRoot
}
return project.file(objRoot)
}
File getSymRoot() {
if (symRoot instanceof File) {
return symRoot
}
return project.file(symRoot)
}
File getSharedPrecompsDir() {
if (sharedPrecompsDir instanceof File) {
return sharedPrecompsDir
}
return project.file(sharedPrecompsDir)
}
File getDerivedDataPath() {
if (derivedDataPath instanceof File) {
return derivedDataPath
}
return project.file(derivedDataPath)
}
void signing(Closure closure) {
ConfigureUtil.configure(closure, this.signing)
}
boolean isSimulatorBuildOf(Type expectedType) {
if (type != expectedType) {
logger.debug("is no simulator build")
return false;
}
logger.debug("is simulator build {}", this.simulator)
return this.simulator;
}
boolean isDeviceBuildOf(Type expectedType) {
if (type != expectedType) {
return false;
}
return !this.simulator
}
void destination(Closure closure) {
Destination destination = new Destination()
ConfigureUtil.configure(closure, destination)
setDestination(destination)
}
void setDestination(def destination) {
_parameters.setDestination(destination)
}
Set getDestinations() {
return _parameters.configuredDestinations
}
void setArch(Object arch) {
if (arch instanceof List) {
logger.debug("Arch is List: " + arch + " - " + arch.getClass().getName())
this.arch = arch;
} else {
logger.debug("Arch is string: " + arch + " - " + arch.getClass().getName())
this.arch = new ArrayList();
this.arch.add(arch.toString());
}
}
void setEnvironment(Object environment) {
if (environment == null) {
return
}
if (environment instanceof Map) {
logger.debug("environment is Map: " + environment + " - " + environment.getClass().getName())
this.environment = environment;
} else {
logger.debug("environment is string: " + environment + " - " + environment.getClass().getName())
this.environment = new HashMap();
String environmentString = environment.toString()
int index = environmentString.indexOf("=")
if (index == -1) {
this.environment.put(environmentString, null)
} else {
this.environment.put(environmentString.substring(0, index),environmentString.substring(index + 1))
}
}
}
void setVersion(String version) {
this.xcodeVersion = version
// check if the version is valid. On creation of the Xcodebuild class an exception is thrown if the version is not valid
xcode = null
//getXcode()
}
String getValueFromInfoPlist(key) {
if (infoPlist != null) {
File infoPlistFile = new File(project.projectDir, infoPlist)
return plistHelper.getValueFromPlist(infoPlistFile, key)
}
/*
try {
logger.debug("project.projectDir {}", project.projectDir)
File infoPlistFile = new File(project.projectDir, infoPlist)
logger.debug("get value {} from plist file {}", key, infoPlistFile)
return commandRunner.runWithResult([
"/usr/libexec/PlistBuddy",
infoPlistFile.absolutePath,
"-c",
"Print :" + key])
} catch (IllegalStateException ex) {
return null
}
*/
}
String getBundleName() {
if (bundleName != null) {
return bundleName
}
bundleName = getValueFromInfoPlist("CFBundleName")
bundleName = variableResolver.resolve(bundleName);
if (StringUtils.isEmpty(bundleName)) {
bundleName = this.productName
}
return bundleName
}
// should be removed an replaced by the xcodebuildParameters.outputPath
File getOutputPath() {
String path = getConfiguration()
if (type == Type.iOS) {
if (simulator) {
path += "-iphonesimulator"
} else {
path += "-iphoneos"
}
}
return new File(getSymRoot(), path)
}
BuildConfiguration getParent(BuildConfiguration buildSettings) {
BuildConfiguration result = buildSettings
File infoPlist = new File(project.projectDir, buildSettings.infoplist);
String bundleIdentifier = plistHelper.getValueFromPlist(infoPlist, "WKCompanionAppBundleIdentifier")
if (bundleIdentifier != null) {
projectSettings.each { String key, BuildTargetConfiguration buildConfiguration ->
BuildConfiguration settings = buildConfiguration.buildSettings[configuration];
if (settings != null && settings.bundleIdentifier.equalsIgnoreCase(bundleIdentifier)) {
result = settings
return
}
}
}
return result;
}
File getApplicationBundle() {
BuildTargetConfiguration buildConfiguration = projectSettings[target]
if (buildConfiguration != null) {
BuildConfiguration buildSettings = buildConfiguration.buildSettings[configuration];
if (buildSettings != null && buildSettings.sdkRoot.equalsIgnoreCase("watchos")) {
BuildConfiguration parent = getParent(buildSettings)
return new File(getOutputPath(), parent.productName + "." + this.productType)
}
}
return new File(getOutputPath(), getBundleName() + "." + this.productType)
}
File getBinary() {
logger.debug("getBinary")
BuildTargetConfiguration buildConfiguration = projectSettings[target]
if (buildConfiguration != null) {
BuildConfiguration buildSettings = buildConfiguration.buildSettings[configuration];
logger.debug("buildSettings: {}", buildSettings)
if (type == Type.macOS) {
return new File(getOutputPath(), buildSettings.productName + ".app/Contents/MacOS/" + buildSettings.productName)
}
return new File(getOutputPath(), buildSettings.productName + ".app/" + buildSettings.productName)
}
return null
}
BuildConfiguration getBuildConfiguration() {
BuildTargetConfiguration buildTargetConfiguration = projectSettings[target]
if (buildTargetConfiguration != null) {
return buildTargetConfiguration.buildSettings[configuration];
}
throw new IllegalStateException("No build configuration found for + target '" + parameters.target + "' and configuration '" + configuration + "'")
}
BuildConfiguration getBuildConfiguration(String bundleIdentifier) {
BuildConfiguration result = null
projectSettings.each() { target, buildTargetConfiguration ->
BuildConfiguration settings = buildTargetConfiguration.buildSettings[configuration]
if (settings != null) {
if (settings.bundleIdentifier == null && settings.infoplist != null) {
String identifier = plistHelper.getValueFromPlist(new File(settings.infoplist), "CFBundleIdentifier")
if (identifier != null && identifier.equalsIgnoreCase(bundleIdentifier)) {
result = settings
return true
}
} else if (settings.bundleIdentifier != null && settings.bundleIdentifier.equalsIgnoreCase(bundleIdentifier)) {
result = settings
return true
}
}
}
return result
}
void setType(String type) {
this.type = Type.typeFromString(type)
}
boolean getSimulator() {
if (type == Type.macOS) {
return false
}
return this.simulator
}
void setSimulator(Object simulator) {
if (simulator instanceof Boolean) {
this.simulator = simulator
return
}
this.simulator = simulator.toString().equalsIgnoreCase("true") || simulator.toString().equalsIgnoreCase("yes")
}
void setProjectFile(def projectFile) {
if (projectFile instanceof File) {
this.projectFile = projectFile
}
this.projectFile = new File(project.projectDir.absolutePath, projectFile)
}
final excludedDirs = ['.svn', '.git', '.hg', '.idea', 'node_modules', '.gradle', 'CMakeFiles']
File getProjectFile() {
if (this.projectFile != null) {
return this.projectFile
}
def file = null
project.projectDir.traverse(
type : ANY,
nameFilter : ~/.*\.xcodeproj$/,
preDir : { if (it.name in excludedDirs) return SKIP_SUBTREE },
visitRoot : true) {
if(file == null)
file = it
}
if(file != null)
return file
return new File(project.projectDir, project.projectDir.list(new SuffixFileFilter(".xcodeproj"))[0])
}
// should be remove in the future, so that every task has its own xcode object
Xcode getXcode() {
if (xcode == null) {
xcode = new Xcode(commandRunner, xcodeVersion)
}
logger.debug("using xcode {}", xcode)
return xcode
}
XcodebuildParameters getXcodebuildParameters() {
def result = new XcodebuildParameters()
result.scheme = this.scheme
result.target = this.target
result.simulator = this.simulator
result.type = this.type
result.workspace = getWorkspace()
result.configuration = this.configuration
result.dstRoot = this.getDstRoot()
result.objRoot = this.getObjRoot()
result.symRoot = this.getSymRoot()
result.sharedPrecompsDir = this.getSharedPrecompsDir()
result.derivedDataPath = this.getDerivedDataPath()
result.additionalParameters = this.additionalParameters
result.devices = this.devices
result.configuredDestinations = this.destinations
result.bitcode = this.bitcode
result.applicationBundle = getApplicationBundle()
if (this.arch != null) {
result.arch = this.arch.clone()
}
return result
}
}