//Construct an XcodeObject and fill it with data from the given reader public XcodeObject(StreamReader reader, Dictionary <string, XcodeObject> uids) { m_uniqueIds = uids; StringBuilder sb = new StringBuilder(); //Object definitions are expected to start with an opening brace, eat it if(reader.Read() != '{'){ Debug.LogError("Object didn't start with opening brace"); } bool quoted = false; //True if Currently inside quotes, ignore special character meanings bool escaped = false; //True if Next character has special meaning ignored bool commented = false; //True if Inside a block comment, ignore everything except end-comment bool isLiteralValue = true; //Value is a literal (as opposed to a container of objects) string key = ""; //Last key read int readChar; //Char read from the stream //Now start parsing for real while(reader.Peek() != -1){ //If currently in a comment if(commented){ //Check if we drop out of comment readChar = reader.Read(); if(readChar == '*' && reader.Peek() == '/'){ reader.Read(); //Eat the end-of-comment '/' commented = false; } continue; //Ignore read character } //Check to see if we should start reading child object instead if(!quoted && reader.Peek() == '{'){ isLiteralValue = false; children[key] = new XcodeObject(reader, m_uniqueIds); //Read child data } //Read next character readChar = reader.Read(); // = : Key finished if(!quoted && readChar == '='){ key = sb.ToString().Trim(); sb.Length = 0; //Clear contents of stringbuilder } // ; : Key-value pair finished else if(!quoted && readChar == ';'){ if(isLiteralValue){ string value = sb.ToString().Trim(); literals[key] = value; } sb.Length = 0; //Clear contents of stringbuilder isLiteralValue = true; } // } : Object finished else if(!quoted && readChar == '}'){ break; } // / : Might be the start of a comment else if(!quoted && readChar == '/'){ //Comment starts if(reader.Peek() == '*'){ reader.Read(); //Eat the * commented = true; } //Comment didn't start, add the '/' else{ sb.Append((char)readChar); } } //Ignore whitespace outside quotes else if(!quoted && char.IsWhiteSpace((char)readChar)){ // Do nothing } // " : Toggle quoted / not else if(!escaped && readChar == '"'){ quoted = !quoted; sb.Append((char)readChar); escaped = false; } // \ : "Escape" next char else if(!escaped && readChar == '\\'){ sb.Append((char)readChar); escaped = true; } //Just another character else{ sb.Append((char)readChar); escaped = false; } } }
//frameworkFileName -- filepath relative to SDKs directory (e.g. "System/Library/Frameworks/CoreTelephony.framework") //framework is put under the "Frameworks" group and added to the main target protected void AddSDKFramework(string frameworkFilename) { //Make the file reference object XcodeObject frameworkObject = new XcodeObject(UniqueIds); frameworkObject.literals["isa"] = "PBXFileReference"; if(frameworkFilename.EndsWith(".dylib")){ frameworkObject.literals["lastKnownFileType"] = "\"compiled.mach-o.dylib\""; } else{ frameworkObject.literals["lastKnownFileType"] = "wrapper.framework"; } frameworkObject.literals["name"] = "\""+Path.GetFileName(frameworkFilename)+"\""; frameworkObject.literals["path"] = "\""+frameworkFilename+"\""; frameworkObject.literals["sourceTree"] = "SDKROOT"; string frameworkUId = GetNewUniqueId(frameworkObject); //Make appear in frameworks group GetGroup("Frameworks").AddLiteralListElement("children", frameworkUId); //Make build reference XcodeObject buildFile = new XcodeObject(UniqueIds); buildFile.literals["isa"] = "PBXBuildFile"; buildFile.literals["fileRef"] = frameworkUId; string buildFileUId = GetNewUniqueId(buildFile); //Add build reference to build phase MainTargetFrameworkPhase.AddLiteralListElement("files", buildFileUId); }
//Provides unique file IDs and ensures the ObjectRoot knows about them. Don't have more than 2^32 files. protected string GetNewUniqueId(XcodeObject requester) { //Find a non-taken UID string uid; do{ uid = m_uidPrefix + m_uidOffset.ToString("X8"); //Hex encode m_uidOffset++; } while(UniqueIds.ContainsKey(uid)); //Assign it UniqueIds[uid] = requester; ObjectRoot.children[uid] = requester; return uid; }
//Copes a directory into the XCode project as a framework -- the directory is referenced directly //as a framework, its contents are not directly referenced. protected void AddFrameworkDirectory(DirectoryInfo srcDir, string destDir, XcodeObject parentGroup) { //Create XCode file ref XcodeObject fileRef = new XcodeObject(UniqueIds); fileRef.literals["isa"] = "PBXFileReference"; fileRef.literals["name"] = "\""+Path.GetFileName(srcDir.Name)+"\""; fileRef.literals["path"] = "\""+srcDir.Name+"\""; fileRef.literals["sourceTree"] = "\"<group>\""; fileRef.literals["lastKnownFileType"] = "wrapper.framework"; //Add to group string fileRefId = GetNewUniqueId(fileRef); parentGroup.AddLiteralListElement("children", fileRefId); //Create Xcode build file ref XcodeObject buildFile = new XcodeObject(UniqueIds); buildFile.literals["isa"] = "PBXBuildFile"; buildFile.literals["fileRef"] = fileRefId; string buildFileId = GetNewUniqueId(buildFile); //Add build reference to resources phase MainTargetFrameworkPhase.AddLiteralListElement("files", buildFileId); //Copy framework contents DirectoryInfo frameworkDir = new DirectoryInfo(Path.Combine(destDir, srcDir.Name)); ExternalProcesses.RunProcess("cp", "-a \"" + srcDir.FullName + "\" \"" + frameworkDir.FullName+ "\""); //Add path of framework's containing directory to search paths AddFrameworkSearchPath(frameworkDir.Parent.FullName); }
protected void AddFile(FileInfo file, string destDir, XcodeObject parentGroup) { string filePath = Path.Combine(destDir, file.Name); if(!File.Exists(filePath)){ //Create XCode file ref XcodeObject fileRef = new XcodeObject(UniqueIds); fileRef.literals["isa"] = "PBXFileReference"; fileRef.literals["name"] = "\""+file.Name+"\""; fileRef.literals["path"] = "\""+file.Name+"\""; fileRef.literals["sourceTree"] = "\"<group>\""; //Add to group string fileRefId = GetNewUniqueId(fileRef); parentGroup.AddLiteralListElement("children", fileRefId); //Create Xcode build file ref XcodeObject buildFile = new XcodeObject(UniqueIds); buildFile.literals["isa"] = "PBXBuildFile"; buildFile.literals["fileRef"] = fileRefId; string buildFileId = GetNewUniqueId(buildFile); //Add build reference to appropriate build phase if(IsSource(file.Name)){ MainTargetSourcePhase.AddLiteralListElement("files", buildFileId); } else if(IsArchive(file.Name)){ MainTargetFrameworkPhase.AddLiteralListElement("files", buildFileId); AddLibrarySearchPath(destDir); } else if(!IsHeader(file.Name)){ MainTargetResourcePhase.AddLiteralListElement("files", buildFileId); } if(EnableBfgAutomaticReferenceCounting && UsesAutomaticReferenceCounting(file.FullName)){ var settingsObj = new XcodeObject(UniqueIds); buildFile.children["settings"] = settingsObj; settingsObj.literals["COMPILER_FLAGS"] = kAutomaticReferenceCountingFlag; } } else{ Console.WriteLine("Overwrote file " + filePath); } //Copy disk file file.CopyTo(filePath, true); }
//Copies a directory and its contents into the XCode project recursively and adds it to the main target. protected void AddDirectoryRecursively(string sourceDir, string destDir, XcodeObject parentGroup) { DirectoryInfo sourceDirInfo = new DirectoryInfo(sourceDir); //Create files foreach(FileInfo file in sourceDirInfo.GetFiles()){ if(IsIgnored(file.Name)){ continue; } else{ AddFile(file, destDir, parentGroup); } } //Recurse foreach(DirectoryInfo subDir in sourceDirInfo.GetDirectories()){ if(IsIgnored(subDir.Name)){ // ... } else if(IsBundle(subDir.Name)){ AddBundle(subDir, destDir, parentGroup); } else if(IsFrameworkDirectory(subDir.Name)){ AddFrameworkDirectory(subDir, destDir, parentGroup); } else{ XcodeObject groupObj = null; DirectoryInfo destSubDir = new DirectoryInfo(Path.Combine(destDir, subDir.Name)); //Set up the dest directory if(!destSubDir.Exists || parentGroup.GetGroup(destSubDir.Name) == null){ destSubDir.Create(); //Make containing Xcode group groupObj = new XcodeObject(UniqueIds); groupObj.literals["isa"] = "PBXGroup"; groupObj.literals["sourceTree"] = "\"<group>\""; groupObj.literals["children"] = "()"; groupObj.literals["path"] = "\""+destSubDir.Name+"\""; string groupObjId = GetNewUniqueId(groupObj); parentGroup.AddLiteralListElement("children", groupObjId); } else{ groupObj = parentGroup.GetGroup(destSubDir.Name); } AddDirectoryRecursively(subDir.FullName, Path.Combine(destDir, subDir.Name), groupObj); } } }
//Adds a directory to the XCode project as a bundle -- the directory is referenced directly as a file, its contents are copied unreferenced protected void AddBundle(DirectoryInfo srcDir, string destDir, XcodeObject parentGroup) { //Create XCode file ref XcodeObject fileRef = new XcodeObject(UniqueIds); fileRef.literals["isa"] = "PBXFileReference"; fileRef.literals["name"] = "\""+srcDir.Name+"\""; fileRef.literals["path"] = "\""+srcDir.Name+"\""; fileRef.literals["sourceTree"] = "\"<group>\""; //Add to group string fileRefId = GetNewUniqueId(fileRef); parentGroup.AddLiteralListElement("children", fileRefId); //Create Xcode build file ref XcodeObject buildFile = new XcodeObject(UniqueIds); buildFile.literals["isa"] = "PBXBuildFile"; buildFile.literals["fileRef"] = fileRefId; string buildFileId = GetNewUniqueId(buildFile); //Add build reference to resources phase MainTargetResourcePhase.AddLiteralListElement("files", buildFileId); DirectoryInfo bundleDir = new DirectoryInfo(Path.Combine(destDir, srcDir.Name)); AddBundleContentsRecursively(srcDir, bundleDir); }
public override bool PostprocessBuild(BuildTarget target, string buildPath, string dataPath) { if (!PathsValid) { Debug.LogError("Paths not valid: " + m_errorMessage); return false; } BuildPath = Path.GetFullPath(buildPath); try{ UniqueIds = new Dictionary<string, XcodeObject>(); //Load pbxproj file Console.WriteLine("Loading .xcodeproj at " + buildPath + "/" + kProjectPath); StreamReader reader = new StreamReader(buildPath + "/" + kProjectPath); reader.ReadLine(); //Eat explicit encoding header XcodeObject proj = new XcodeObject(reader, UniqueIds); reader.Close(); //Setup important properties FindRequiredFrameworks(); ObjectRoot = proj.children["objects"]; ObjectRoot.DefinePreExistingObjects(); ProjectObject = ObjectRoot.children[proj.literals["rootObject"]]; RootGroup = ObjectRoot.children[ProjectObject.literals["mainGroup"]]; List<XcodeObject> mainTargetBuildPhases = ProjectObject.ParseIDList("targets")[0].ParseIDList("buildPhases"); MainTargetFrameworkPhase = mainTargetBuildPhases.Find(delegate(XcodeObject obj){return obj.literals["isa"] == "PBXFrameworksBuildPhase";}); MainTargetResourcePhase = mainTargetBuildPhases.Find(delegate(XcodeObject obj){return obj.literals["isa"] == "PBXResourcesBuildPhase";}); MainTargetSourcePhase = mainTargetBuildPhases.Find(delegate(XcodeObject obj){return obj.literals["isa"] == "PBXSourcesBuildPhase";}); //Remove secondary targets (currently only the non-functional simulator target) string[] buildTargetIDs = XcodeObject.SplitPCSVList(ProjectObject.literals["targets"]); ProjectObject.literals.Remove("targets"); ProjectObject.AddLiteralListElement("targets", buildTargetIDs[0]); //Add required frameworks Console.WriteLine("Adding required frameworks"); foreach(string frameworkPath in RequiredFrameworks){ Console.WriteLine(">\t" + frameworkPath); AddSDKFramework(frameworkPath); } //Add files Console.WriteLine("Adding specified directories and frameworks"); for(int i = 0; i < m_paths.Length; i++){ if (!m_paths[i].isFrameworkLink) { Console.WriteLine("DIR>\t"+m_paths[i].path); AddDirectoryRecursively(m_paths[i].path, buildPath, RootGroup); } else if (!RequiredFrameworks.Contains(m_paths[i].path)) { Console.WriteLine("FWK>\t"+m_paths[i].path); AddSDKFramework(m_paths[i].path); } } //Save modified file Console.WriteLine("Saving .xcodeproj file"); StreamWriter writer = new StreamWriter(buildPath + "/" + kProjectPath); //Overwrite .pbxproj file writer.Write("// !$*UTF8*$!\n"); //Explicit format header proj.Save(writer, 0); writer.Close(); } catch(Exception e){ Debug.LogError(e.ToString()); return false; } return true; }