All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbakery.XcodeBuildPluginExtension.groovy Maven / Gradle / Ivy

Go to download

XCode-Plugin is a plugin to allow custom XCode projects to build as generated by CMake

There is a newer version: 0.0.201
Show newest version
/*
 * 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


/*

^
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)
	}

	File getProjectFile() {
		if (this.projectFile != null) {
			return this.projectFile
		}
		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
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy