XCode 签名配置的快速设置工具
之前看到过一篇无证做真机测试的文章,很受用~
不过因为当时手贱,把默认的“iPhone Developer”签名 改成了自己的名字
直接导致后来的每一个 XCode 项目,我想在真机上面看效果的话都要重新设置签名配置。
当然,是可以按照那篇文章把这个名字再修改回来,不过我当时懒,就一直这么弄着了
如今经过了那么长的时间,我电脑里面存储的很多工程都沿用了蛋疼的配置
所以一时半会儿要修改过来,也是一件很麻烦的事情。
而且,最近我们团队有通过 Versions 做项目管理进行协作开发~
好不容易解决了多人提交冲突的问题,但是还是存在一些令人不满意的地方,
那就是签名的问题,我的另一个伙计真机测试的话是用的他自己合法的签名
直接导致我更新或者他更新了以后,各自的签名被弄坏得重复的做一些签名的设置~
还有祸不单行,XCode 修改签名的时候有时修改不动,要关了再打开才能改的动,是个bug~
自从上次观察过 project.pbxproj 文件以后,我就发现签名的配置数据也是以明文的方式保存在这个里面的~
那么,完全有可能用 Java 写一个文本处理工具将签名配置那一段做文本替换,置换为我常用的签名配置。
开始我想的使用 Java 的正则表达式,但是一路都不成功。
不要还是因为我对正则表达式的适用范围了解地还不够深刻~
正则表达式擅长于做文本的行内处理,那种跨多行文本的情况用正则是不能取得很好效果的。
主要还是因为 Java 正则表达式的通配符 “.” 只能匹配除 \n 以外的所有其他字符所致~
后来我看到签名配置那段的开头和结尾都做了固定写法的注释,
这样的话我便抛弃了正则表达式的解决方案,直接用 String.indexOfString() 来做实现了
接下来基本上就是很简单的事情了,几行代码便解决了所有问题,下面上代码:
RepairWonderPipe.java
package org.bruce.xcproj.codesign.repair; import java.io.File; import java.io.InputStream; /** * @author Bruce Yang * 仅限于处理 WonderPipe 工程(因为会将工程的某些设置抹掉)~ */ public class RepairWonderPipe { public static final String PROJECT_DIR = "/Users/user/SVN/WonderPipe"; /** * 检查传入的文件目录是否为项目文件夹~ * @param dirProject * @return */ public static boolean isProjectDir(String strProjectDir) { File dirProject = new File(strProjectDir); if(dirProject.exists() && dirProject.isDirectory()) { for(File fileItem : dirProject.listFiles()) { if(fileItem.getName().endsWith(".xcodeproj")) { return true; } } } System.out.println("不是项目文件夹~"); return false; } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub // 1.检查是否为项目文件夹~ if(!isProjectDir(PROJECT_DIR)) { System.err.println("不是 XCode 工程文件的根目录~"); return; } // 2.还是不确定能不能取到包下面的文件~ StringBuffer sbPath = new StringBuffer(PROJECT_DIR); sbPath.append(File.separator).append("WonderPipe.xcodeproj"); sbPath.append(File.separator).append("project.pbxproj"); String strPath = sbPath.toString(); File f = new File(strPath); if(!f.exists()) { System.err.println("文件不存在: " + strPath); return; } // 3.将文件内容读成字符串~ String fContents = StringFileBridge.file2String(f, "utf-8"); // System.out.println(fContents); // 4.处理~ String strBegin = "/* Begin XCBuildConfiguration section */"; int iBeginIndex = fContents.indexOf(strBegin); String strEnd = "/* End XCBuildConfiguration section */"; int iEndIndex = fContents.indexOf(strEnd) + strEnd.length(); String strToBeReplaced = fContents.substring(iBeginIndex, iEndIndex); // System.err.println(strToBeReplaced); String strResPath = "/org/bruce/xcproj/codesign/repair/XCBuildConfigurationBakup.txt"; InputStream is = RepairWonderPipe.class.getResourceAsStream(strResPath); String strCfgTemplate = StringFileBridge.stream2String(is, "utf-8"); // System.err.println(strCfgTemplate); String strOutput = fContents.replace(strToBeReplaced, strCfgTemplate); // 5.将处理完毕的文件写回原文件~ StringFileBridge.string2File(strOutput, f); // test0(); // test1(); // test2(); } /** * 特殊字符 * 的匹配,前面加双反斜杠~ */ public static void test0() { String str = "* sdfsdfsd *"; String reg = "\\* sdfsdfsd \\*"; if(str.matches(reg)) { System.out.println("no!"); } } public static void test1() { String str = "/哈哈/"; String reg = "/.*/"; if(str.matches(reg)) { System.out.println("哈哈"); } } /** * 结论:正则表达式不适合用于处理多行的文本~ */ public static void test2() { // String str = "123123\n/* 嘻 */\n1231231"; String str = "\n/* 嘻 */\n"; String reg = "[.\n]*/\\* 嘻 \\*/[.\n]*"; if(str.matches(reg)) { System.out.println("嘻"); } } }StringFileBridge.java
package org.bruce.xcproj.codesign.repair; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; /** * 字符串与文件相互转换工具 * @author leizhimin 2009-7-14 15:54:18 */ public class StringFileBridge { /** * 读取文件为一个内存字符串,保持文件原有的换行格式 * @param file 文件对象 * @param charset 文件字符集编码 * @return 文件内容的字符串 */ public static String file2String(File file, String charset) { StringBuffer sb = new StringBuffer(); try { FileInputStream fis = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(fis, charset); BufferedReader br = new BufferedReader(isr); LineNumberReader reader = new LineNumberReader(br); String line; while ((line = reader.readLine()) != null) { sb.append(line).append(System.getProperty("line.separator")); } reader.close(); } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } /** * @author Bruce Yang * 该方法用于配合上面的方法使用。 * @param unicodeStr * @return */ public static String changeEncode(String unicodeStr, String charset) { String utf8Str = null; try { utf8Str = new String(unicodeStr.getBytes(charset)); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return utf8Str; } /** * 将字符串存储为一个文件,当文件不存在时候,自动创建该文件,当文件已存在时候,重写文件的内容,特定情况下,还与操作系统的权限有关。 * @param text 字符串 * @param distFile 存储的目标文件 * @return 当存储正确无误时返回true,否则返回false */ public static boolean string2File(String text, File distFile) { if (!distFile.getParentFile().exists()) { distFile.getParentFile().mkdirs(); } BufferedReader br = null; BufferedWriter bw = null; boolean flag = true; try { br = new BufferedReader(new StringReader(text)); bw = new BufferedWriter(new FileWriter(distFile)); char buf[] = new char[1024 * 64]; // 字符缓冲区 int len; while ((len = br.read(buf)) != -1) { bw.write(buf, 0, len); } bw.flush(); br.close(); bw.close(); } catch (IOException e) { flag = false; e.printStackTrace(); System.out.println("将字符串写入文件发生异常!"); } return flag; } /** * 文件转换为字符串 * @param in 字节流 * @param charset 文件的字符集 * @return 文件内容 */ public static String stream2String(InputStream in, String charset) { StringBuffer sb = new StringBuffer(); try { Reader reader = new InputStreamReader(in, charset); int length = 0; for (char[] c = new char[1024]; (length = reader.read(c)) != -1;) { sb.append(c, 0, length); } reader.close(); } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } /** * @param args */ public static void main(String[] args) { String x = file2String(new File("/Users/user/Desktop/123.java"), "GBK"); System.out.println(x); boolean b = string2File(x, new File("/Users/user/Desktop/1234.java")); System.out.println(b); } }
XCBuildConfigurationBakup.txt (PS: 和前面的两个类放在同一个 package 下面~)
/* Begin XCBuildConfiguration section */ BAAEAE4815A89BC600FF66D7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( DEBUG, "COCOS2D_DEBUG=1", ); GCC_SYMBOLS_PRIVATE_EXTERN = NO; GCC_VERSION = com.apple.compilers.llvmgcc42; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 4.3; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; SDKROOT = iphoneos; }; name = Debug; }; BAAEAE4915A89BC600FF66D7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; CODE_SIGN_ENTITLEMENTS = ""; CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_PREPROCESSOR_DEFINITIONS = NDEBUG; GCC_VERSION = com.apple.compilers.llvmgcc42; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 4.3; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; SDKROOT = iphoneos; }; name = Release; }; BAAEAE4B15A89BC600FF66D7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; CODE_SIGN_ENTITLEMENTS = Entitlements.plist; CODE_SIGN_IDENTITY = yang3wei; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = yang3wei; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = WonderPipe/Prefix.pch; "GCC_THUMB_SUPPORT[arch=armv6]" = ""; GCC_VERSION = com.apple.compilers.llvmgcc42; INFOPLIST_FILE = WonderPipe/Resources/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 4.3; OTHER_LDFLAGS = "-lz"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = "\"WonderPipe/libs\""; WRAPPER_EXTENSION = app; }; name = Debug; }; BAAEAE4C15A89BC600FF66D7 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = YES; CODE_SIGN_ENTITLEMENTS = Entitlements.plist; CODE_SIGN_IDENTITY = yang3wei; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = yang3wei; COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = WonderPipe/Prefix.pch; "GCC_THUMB_SUPPORT[arch=armv6]" = ""; GCC_VERSION = com.apple.compilers.llvmgcc42; INFOPLIST_FILE = WonderPipe/Resources/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 4.3; OTHER_LDFLAGS = "-lz"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = "\"WonderPipe/libs\""; VALIDATE_PRODUCT = YES; WRAPPER_EXTENSION = app; }; name = Release; }; /* End XCBuildConfiguration section */