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

ca.mestevens.ios.utils.XcodeProjectUtil Maven / Gradle / Ivy

There is a newer version: 0.9.4
Show newest version
package ca.mestevens.ios.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import lombok.Data;

import org.apache.maven.plugin.MojoExecutionException;

import ca.mestevens.ios.models.Dependency;
import ca.mestevens.ios.xcode.parser.exceptions.InvalidObjectFormatException;
import ca.mestevens.ios.xcode.parser.models.CommentedIdentifier;
import ca.mestevens.ios.xcode.parser.models.PBXBuildFile;
import ca.mestevens.ios.xcode.parser.models.PBXBuildPhase;
import ca.mestevens.ios.xcode.parser.models.PBXFileElement;
import ca.mestevens.ios.xcode.parser.models.PBXTarget;
import ca.mestevens.ios.xcode.parser.models.XCConfigurationList;
import ca.mestevens.ios.xcode.parser.models.XCodeProject;

@Data
public class XcodeProjectUtil {
	
	private String pbxProjLocation;
	private XCodeProject xcodeProject;
	
	public XcodeProjectUtil(String pbxProjLocation) throws InvalidObjectFormatException {
		this.pbxProjLocation = pbxProjLocation;
		this.xcodeProject = new XCodeProject(pbxProjLocation);
	}
	
	public boolean containsTestTarget() {
		List targets = xcodeProject.getNativeTargets();
		for(PBXTarget target : targets) {
			if (target.getProductType() != null && target.getProductType().contains("com.apple.product-type.bundle.unit-test")) {
				return true;
			}
		}
		return false;
	}

	public void addDependenciesToTarget(String targetName, List dynamicFrameworks, List staticFrameworks, List libraries, List externalDependencies) throws MojoExecutionException {
		try {
			List masterFileList = new ArrayList();
			if (libraries == null) {
				libraries = new ArrayList();
			}
			if (staticFrameworks == null) {
				staticFrameworks = new ArrayList();
			}
			if (dynamicFrameworks == null) {
				dynamicFrameworks = new ArrayList();
			}
			if (externalDependencies == null) {
				externalDependencies = new ArrayList();
			}
			masterFileList.addAll(libraries);
			masterFileList.addAll(staticFrameworks);
			masterFileList.addAll(dynamicFrameworks);
			
			//Add the file/build file references
			List fileReferenceIdentifiers = new ArrayList();
			List libraryFileReferences = addFileReferences(libraries);
			List staticFrameworkFileReferences = addFileReferences(staticFrameworks);
			List dynamicFrameworkFileReferences = addFileReferences(dynamicFrameworks);
			List externalFileReferences = addExternalFileReferences(externalDependencies);
			fileReferenceIdentifiers.addAll(libraryFileReferences);
			fileReferenceIdentifiers.addAll(staticFrameworkFileReferences);
			fileReferenceIdentifiers.addAll(dynamicFrameworkFileReferences);
			fileReferenceIdentifiers.addAll(externalFileReferences);
			List libraryBuildIdentifiers = addBuildFiles(libraryFileReferences, false);
			List staticFrameworkBuildIdentifiers = addBuildFiles(staticFrameworkFileReferences, false);
			List externalFrameworkBuildIdentifiers = addBuildFiles(externalFileReferences, false);
			List dynamicFrameworkBuildIdentifiers = addBuildFiles(dynamicFrameworkFileReferences, true);
			//Get the target
			PBXTarget target = getNativeTarget(targetName);
			//Link the static libraries
			linkLibraries(target.getReference().getIdentifier(), libraryBuildIdentifiers);
			linkLibraries(target.getReference().getIdentifier(), staticFrameworkBuildIdentifiers);
			linkLibraries(target.getReference().getIdentifier(), externalFrameworkBuildIdentifiers);
			//Embed the dynamic libraries
			embedLibraries(target, dynamicFrameworkBuildIdentifiers);
			//Add the properties to the build configuration
			XCConfigurationList configuration = xcodeProject.getConfigurationListWithIdentifier(target.getBuildConfigurationList().getIdentifier());
			for(CommentedIdentifier identifier : configuration.getBuildConfigurations()) {
				if ((dynamicFrameworks != null && dynamicFrameworks.size() > 0) || (staticFrameworks != null && staticFrameworks.size() > 0)) {
					addPropertyToList(identifier.getIdentifier(), "FRAMEWORK_SEARCH_PATHS", "\"${PROJECT_DIR}/target/xcode-dependencies/frameworks/**\"");
					addPropertyToString(identifier.getIdentifier(), "LD_RUNPATH_SEARCH_PATHS", "@loader_path/Frameworks");
					addPropertyToString(identifier.getIdentifier(), "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks");
				}
				if (libraries != null && libraries.size() > 0) {
					addPropertyToList(identifier.getIdentifier(), "HEADER_SEARCH_PATHS", "\"${PROJECT_DIR}/target/xcode-dependencies/libraries/**\"");
					addPropertyToList(identifier.getIdentifier(), "LIBRARY_SEARCH_PATHS", "\"${PROJECT_DIR}/target/xcode-dependencies/libraries/**\"");
				}
			}
			if (fileReferenceIdentifiers.size() > 0) {
				createGroup(fileReferenceIdentifiers);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new MojoExecutionException(ex.getMessage());
		}
	}
	
	public void addPropertyToList(String buildConfiguration, String key, String value) {
		List propertyList = xcodeProject.getBuildConfigurationPropertyAsList(buildConfiguration, key);
		if (propertyList == null) {
			propertyList = new ArrayList();
		}
		if (!propertyList.contains(value)) {
			propertyList.add(value);
			xcodeProject.setBuildConfigurationProperty(buildConfiguration, key, propertyList);
		}
	}
	
	public void addPropertyToString(String buildConfiguration, String key, String value) {
		String property = xcodeProject.getBuildConfigurationProperty(buildConfiguration, key);
		if (property != null) {
			if (property.startsWith("\"")) {
				property = property.substring(1);
			}
			if (property.endsWith("\"")) {
				property = property.substring(0, property.length() - 1);
			}
			property = property.trim();
			if (!property.contains(value)) {
				property = property.concat(" " + value);
			}
			property = "\"" + property + "\"";
			xcodeProject.setBuildConfigurationProperty(buildConfiguration, key, property);
		} else {
			xcodeProject.setBuildConfigurationProperty(buildConfiguration, key, "\"" + value + "\"");
		}
	}
	
	public List addBuildFiles(List fileReferences, boolean embed) {
		List buildFileReferences = new ArrayList();
		String xcodeProjLocation = pbxProjLocation.substring(0, pbxProjLocation.lastIndexOf("/project.pbxproj"));
		for(CommentedIdentifier fileReference : fileReferences) {
			boolean skip = false;
			for(PBXFileElement fileElement : xcodeProject.getFileReferences()) {
				if (fileElement.getReference().getIdentifier().equals(fileReference.getIdentifier())) {
					String path = fileElement.getPath();
					if (path.startsWith("\"") && path.endsWith("\"")) {
						path = path.substring(1);
						path = path.substring(0, path.length() - 1);
					}
					String fileName = fileElement.getReference().getComment();
					if (fileName.endsWith(".framework")) {
						fileName = fileName.substring(0, fileName.lastIndexOf("."));
						path += "/" + fileName;
					}		
					File file = new File(xcodeProjLocation + "/../" + path);
					if (fileElement.getSourceTree().equals("SOURCE_ROOT") && !file.exists()) {
						skip = true;
					}
				}
			}
			if (skip) {
				continue;
			}
			List buildFiles = xcodeProject.getBuildFileWithFileRef(fileReference.getIdentifier());
			//Check for duplicates and remove them
			if (buildFiles.size() > 1) {
				Iterator buildFileIterator = buildFiles.iterator();
				List uniqueBuildFiles = new ArrayList();
				uniqueBuildFiles.add(buildFileIterator.next());
				while(buildFileIterator.hasNext()) {
					PBXBuildFile nextBuildFile = buildFileIterator.next();
					boolean foundBuildFile = false;
					for(PBXBuildFile uniqueBuildFile : uniqueBuildFiles) {
						if (uniqueBuildFile.getFileRef().equals(nextBuildFile.getFileRef()) && uniqueBuildFile.getSettings().equals(nextBuildFile.getSettings())) {
							foundBuildFile = true;
						}
					}
					if (foundBuildFile) {
						//Remove the file
						buildFileIterator.remove();
						removeBuildFile(nextBuildFile.getReference().getIdentifier());
					} else {
						uniqueBuildFiles.add(nextBuildFile);
					}
				}
			}
			PBXBuildFile buildFile = null;
			for (PBXBuildFile existingFile : buildFiles) {
				if (existingFile.getReference().getComment().contains(fileReference.getComment() + " in Frameworks")) {
					buildFile = existingFile;
				} else if (existingFile.getReference().getComment().contains(fileReference.getComment() + " in Embed Frameworks")) {
					buildFile = existingFile;
				}
			}
			if (buildFile == null && embed) {
				buildFile = new PBXBuildFile(fileReference.getComment() + " in Embed Frameworks", fileReference.getIdentifier());
				buildFile.getSettings().put("ATTRIBUTES", "(CodeSignOnCopy, )");
				xcodeProject.getBuildFiles().add(buildFile);
			} else if (buildFile == null) {
				buildFile = new PBXBuildFile(fileReference.getComment() + " in Frameworks", fileReference.getIdentifier());
				xcodeProject.getBuildFiles().add(buildFile);
			}
			buildFileReferences.add(buildFile.getReference());
		}
		return buildFileReferences;
	}
	
	public List addFileReferences(List files) {
		List fileReferences = new ArrayList();
		for (File dependencyFile : files) {
			if (!dependencyFile.exists()) {
				continue;
			}
			String frameworkPath = dependencyFile.getAbsolutePath().substring(dependencyFile.getAbsolutePath().lastIndexOf("target"));
			PBXFileElement fileReference = xcodeProject.getFileReferenceWithPath(frameworkPath);
			if (fileReference == null) {
				fileReference = xcodeProject.getFileReferenceWithPath("\"" + frameworkPath + "\"");
				if (fileReference == null) {
					fileReference = xcodeProject.createFileReference(frameworkPath, "SOURCE_ROOT");
				}
			}
			fileReferences.add(fileReference.getReference());
		}
		return fileReferences;
	}
	
	public List addExternalFileReferences(List dependencies) {
		List fileReferences = new ArrayList();
		for (Dependency dependencyFile : dependencies) {
			String path = dependencyFile.getPath();
			if (path == null) {
				path = "System/Library/Frameworks/" + dependencyFile.getName() + ".framework";
			}
			PBXFileElement fileReference = xcodeProject.getFileReferenceWithPath(path);
			if (fileReference == null) {
				fileReference = xcodeProject.getFileReferenceWithPath("\"" + path + "\"");
				if (fileReference == null) {
					String sourceTree = "SDKROOT";
					if (!(dependencyFile.getPath() == null)) {
						sourceTree = "SOURCE_ROOT";
					}
					fileReference = xcodeProject.createFileReference(path, sourceTree);
				}
			}
			fileReferences.add(fileReference.getReference());
		}
		return fileReferences;
	}
	
	public void createGroup(List identifiers) {
		//Add/Edit the Frameworks group
		String frameworkGroupIdentifier = null;
		for(PBXFileElement group : xcodeProject.getGroups()) {
			if (group.getName() != null && (group.getName().equals("Frameworks") || group.getName().equals("\"Frameworks\""))) {
				frameworkGroupIdentifier = group.getReference().getIdentifier();
			}
		}
		
		if (frameworkGroupIdentifier != null) {
			PBXFileElement frameworkGroup = xcodeProject.getGroupWithIdentifier(frameworkGroupIdentifier);
			for (CommentedIdentifier fileReference : identifiers) {
				boolean found = false;
				for (CommentedIdentifier child : frameworkGroup.getChildren()) {
					if (child.getComment().equals(fileReference.getComment())) {
						found = true;
					}
				}
				if (!found) {
					frameworkGroup.addChild(fileReference);
				}
			}
		} else {
			String mainGroupIdentifier = xcodeProject.getProject().getMainGroup().getIdentifier();
			xcodeProject.createGroup("Frameworks", identifiers, mainGroupIdentifier);
		}
	}
	
	public void linkLibraries(String targetIdentifier, List identifiers) {
		//Get the configuration list identifier for the first target
		String existingFrameworksPhaseId = null;
		PBXTarget nativeTarget = xcodeProject.getNativeTargetWithIdentifier(targetIdentifier);
		for(CommentedIdentifier buildPhaseIdentifier : nativeTarget.getBuildPhases()) {
			if (buildPhaseIdentifier.getComment().equals("Frameworks")) {
				existingFrameworksPhaseId = buildPhaseIdentifier.getIdentifier();
				break;
			}
		}
		//Add the framework build files to the frameworks build phase
		PBXBuildPhase frameworksBuildPhase = xcodeProject.getFrameworksBuildPhaseWithIdentifier(existingFrameworksPhaseId);
		if (frameworksBuildPhase.getReference().getIdentifier().equals(existingFrameworksPhaseId)) {
			for(CommentedIdentifier identifier : identifiers) {
				if (!frameworksBuildPhase.getFiles().contains(identifier) && !frameworksBuildPhase.getFiles().contains("\"" + identifier + "\"")) {
					frameworksBuildPhase.getFiles().add(identifier);
				}
			}
		}
	}
	
	public void embedLibraries(PBXTarget target, List identifiers) {
		//If the Embed Frameworks phase doesn't exist, create it
		boolean foundExistingPhase = false;
		String copyFrameworksBuildPhaseIdentifier = null;
		for(CommentedIdentifier buildPhase : target.getBuildPhases()) {
			if (buildPhase.getComment().contains("Embed Frameworks")) {
				copyFrameworksBuildPhaseIdentifier = buildPhase.getIdentifier();
			}
		}
		for(PBXBuildPhase copyFilesBuildPhase : xcodeProject.getCopyFilesBuildPhases()) {
			if (copyFilesBuildPhase.getReference().getIdentifier().equals(copyFrameworksBuildPhaseIdentifier)) {
				for(CommentedIdentifier identifier : identifiers) {
					boolean foundFile = false;
					for (CommentedIdentifier fileIdentifier : copyFilesBuildPhase.getFiles()) {
						if (fileIdentifier.getComment().equals(identifier.getComment())) {
							foundFile = true;
						}
					}
					if (!foundFile) {
						copyFilesBuildPhase.getFiles().add(identifier);
					}
				}
				foundExistingPhase = true;
			}
		}
		if (!foundExistingPhase && identifiers.size() > 0) {
			PBXBuildPhase copyFrameworksBuildPhase = new PBXBuildPhase("PBXCopyFilesBuildPhase", "\"Embed Frameworks\"", identifiers, "\"\"", 10);
			xcodeProject.addCopyFilesBuildPhase(target.getReference().getIdentifier(), copyFrameworksBuildPhase);
		}
	}
	
	public PBXTarget getNativeTarget(String targetName) {
		for (PBXTarget target : xcodeProject.getNativeTargets()) {
			if (target.getName() != null && (target.getName().equals(targetName) || target.getName().equals("\"" + targetName + "\""))) {
				return target;
			}
		}
		return null;
	}
	
	public void removeBuildFile(String identifier) {
		List buildFiles = xcodeProject.getBuildFiles();
		Iterator buildFileIt = buildFiles.iterator();
		while(buildFileIt.hasNext()) {
			PBXBuildFile buildFile = buildFileIt.next();
			if (buildFile.getReference().getIdentifier().equals(identifier)) {
				buildFileIt.remove();
			}
		}
		List groups = xcodeProject.getGroups();
		for (PBXFileElement group : groups) {
			Iterator commentedIdentifierIt = group.getChildren().iterator();
			while(commentedIdentifierIt.hasNext()) {
				CommentedIdentifier commentedIdentifier = commentedIdentifierIt.next();
				if (commentedIdentifier.getIdentifier().equals(identifier)) {
					commentedIdentifierIt.remove();
				}
			}
		}
		List frameworksBuildPhases = xcodeProject.getFrameworksBuildPhases();
		for (PBXBuildPhase frameworksBuildPhase : frameworksBuildPhases) {
			Iterator commentedIdentifierIt = frameworksBuildPhase.getFiles().iterator();
			while(commentedIdentifierIt.hasNext()) {
				CommentedIdentifier commentedIdentifier = commentedIdentifierIt.next();
				if (commentedIdentifier.getIdentifier().equals(identifier)) {
					commentedIdentifierIt.remove();
				}
			}
		}
		List copyFilesBuildPhases = xcodeProject.getCopyFilesBuildPhases();
		for (PBXBuildPhase copyFilesBuildPhase : copyFilesBuildPhases) {
			Iterator commentedIdentifierIt = copyFilesBuildPhase.getFiles().iterator();
			while(commentedIdentifierIt.hasNext()) {
				CommentedIdentifier commentedIdentifier = commentedIdentifierIt.next();
				if (commentedIdentifier.getIdentifier().equals(identifier)) {
					commentedIdentifierIt.remove();
				}
			}
		}
	}
	
	public void writeProject() throws IOException {
		Files.write(Paths.get(pbxProjLocation), xcodeProject.toString().getBytes());
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy