├── LLVM_Clang ├── Clang Plugin 之 Debug.md ├── Examples │ ├── QTPlugin │ │ ├── CMakeLists.txt │ │ └── QTPlugin.cpp │ └── QTPluginTooling │ │ ├── CMakeLists.txt │ │ └── QTPluginTooling.cpp └── LLVM & Clang 入门.md ├── Pic ├── clang_AST.png ├── clang_executables_tooling.png ├── clang_ouput.png ├── clang_plugin_diagnostics.png ├── clang_tooling_cmakelists.png ├── clang_tooling_debuging.png ├── clang_tooling_xcode_schene_arguments.png ├── code_clang_plugin.png ├── create_clang_plugin_0.png ├── create_clang_plugin_1.png ├── create_clang_plugin_2.png ├── ios │ ├── 160ed39277c58076.png │ ├── 160ed39dac1bfea0.png │ ├── 160ed3a2a977adc4.png │ ├── 160ed3bde0a93494.png │ ├── 160ed3d277057ae1.png │ ├── 160ed6f6d366332a.png │ ├── 160ed734379c2e01.png │ ├── 160ed7bbe09085cd.png │ ├── 160ed81d2b12988a.png │ ├── 160ed8f2edde989c.png │ ├── 160ed91244f83b13.png │ ├── 160ee2a30718e52a.png │ ├── 160ee2a9d754e46f.png │ ├── 160ee2f3888ec01e.png │ ├── 161301cd070af847.png │ ├── 16130259c16487a4.png │ ├── 16170be40bdda6f2.png │ ├── 16170bef5ace5962.png │ ├── 1618448a7403100f.png │ ├── 161c09f5e3469ce4.png │ ├── 1623e39ade981eb6.png │ ├── 1624403c83678104.png │ ├── 162440e866880e84.png │ ├── 1624430835219f66.png │ └── 1624435303eade8b.png ├── llvm_clang_all_build.png ├── terminal_console_log.png ├── xcode_add_user_defined_settings.png ├── xcode_added_user_defined_cc_cxx.png ├── xcode_clang_plugin_code.png ├── xcode_compiler_error.png ├── xcode_compiler_process.jpeg ├── xcode_compiler_process_2.png ├── xcode_compiler_process_3.png ├── xcode_enable_index_wihle_building_functionality_no.png ├── xcode_index_store_path_error.png ├── xcode_load_plugin_result.png ├── xcode_optimize.png └── xcode_other_cflags.png ├── README.md ├── Shader ├── Pic │ ├── 07EACBD3-9938-43E9-A398-A85E6D3239A6.jpg │ ├── 0DE4BA16-DDC6-4E09-AA6D-AC575323D8E4.png │ ├── 1F0E0949-32F3-4D4B-81AD-AD6B5500D37E.jpg │ ├── 2C9EFD01-0E74-4A68-A3C1-DDD2CAB0B019.png │ ├── 3622A2E4-AE32-4C45-B32E-6444BB868751.png │ ├── 3C599202-CA10-4292-98F3-F0BD859E793D.png │ ├── 438BDB5F-8829-4687-9576-EA4A0AEEAC77.png │ ├── 4A8464B9-64D6-4A9B-8AE9-0BECBD93FA03.png │ ├── 4CC06D2C-1707-4F0E-8194-6802333DFFE8.png │ ├── 526EF765-1589-4D0A-85E3-AE5998B6D235.png │ ├── 629AE65D-4F06-40F9-B044-679FC22BB68A.png │ ├── 68E16F96-99EC-467F-B16C-6A62717B940A.png │ ├── 78975E1C-2C3E-4714-B075-7211453B6038.png │ ├── 8271BB78-D1A9-4511-9ED7-662E199DFDBA.png │ ├── 94C91BDE-6E5F-492E-9CF7-EBCDF30B466E.png │ ├── 973883FD-33FF-484D-80DA-E5AFB640C2C2.jpg │ ├── A96C901D-A4DA-4306-91E0-254485A68C6F.png │ ├── AF86A5F7-B324-45C5-A912-491FD5A62045.png │ ├── BC8C1D7F-4E86-4F62-9C49-CC1F6DA33FD8.jpg │ ├── C25AA265-D417-4208-AF7A-6030BAE3CDC9.png │ ├── CC226FC0-6D7C-4889-B397-EF80189DDB3B.png │ ├── D170E78A-7AEE-4766-B85D-72FC954524AF.png │ ├── EAE2CF2B-BE19-445B-85B4-26E478EBF9CE.png │ └── FD1887D1-5F60-45FC-A2F7-BEB9C0E7E299.png └── Shader_Blend.md └── iOS ├── Examples └── CDDemo │ ├── CDDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── CDDemo copy.xcscheme │ │ └── CDDemo.xcscheme │ ├── CDDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-1024.png │ │ │ ├── Icon-20@2x.png │ │ │ ├── Icon-20@3x.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-40@3x.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-Small.png │ │ │ ├── Icon-Small@2x.png │ │ │ ├── Icon-Small@3x.png │ │ │ ├── Icon.png │ │ │ └── Icon@2x.png │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── CDDemo2Info.plist │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m │ ├── Gemfile │ ├── Gemfile.lock │ └── fastlane │ ├── .env.TargetA │ ├── .env.TargetB │ ├── Appfile │ ├── Deliverfile │ ├── Fastfile │ ├── README.md │ ├── metadata │ ├── copyright.txt │ ├── primary_category.txt │ ├── primary_first_sub_category.txt │ ├── primary_second_sub_category.txt │ ├── review_information │ │ ├── demo_password.txt │ │ ├── demo_user.txt │ │ ├── email_address.txt │ │ ├── first_name.txt │ │ ├── last_name.txt │ │ ├── notes.txt │ │ └── phone_number.txt │ ├── secondary_category.txt │ ├── secondary_first_sub_category.txt │ ├── secondary_second_sub_category.txt │ ├── trade_representative_contact_information │ │ ├── address_line1.txt │ │ ├── city_name.txt │ │ ├── country.txt │ │ ├── is_displayed_on_app_store.txt │ │ ├── postal_code.txt │ │ ├── state.txt │ │ └── trade_name.txt │ └── zh-Hans │ │ ├── description.txt │ │ ├── keywords.txt │ │ ├── marketing_url.txt │ │ ├── name.txt │ │ ├── privacy_url.txt │ │ ├── promotional_text.txt │ │ ├── release_notes.txt │ │ ├── subtitle.txt │ │ └── support_url.txt │ └── screenshots │ └── README.txt ├── ios-fastlane.md └── ios-performance-optimization-summary.md /LLVM_Clang/Clang Plugin 之 Debug.md: -------------------------------------------------------------------------------- 1 | # Clang Plugin 之 Debug 2 | 3 | 前面一篇文章 [LLVM & Clang 入门](https://github.com/CYBoys/Blogs/blob/master/LLVM_Clang/LLVM%20%26%20Clang%20%E5%85%A5%E9%97%A8.md) 讲了如何编写一个 Clang 插件,然后将插件编译成一个`.dylib`的动态链接库。集成到 Xcode 中就可以看到效果(正确的结果)。 4 | 5 | 在得到正确结果的过程中,必不可少的一步就是`Debug`,没有任何程序是一蹴而就的,除非你`printf`一个`"Hello, World!"`,说不定你的`world`还写成了`word`。 6 | 7 | 在使用`Plugin`的模式下我们是不能打断点进行 Debug 的,但是我们可以在代码中加日志,然后在终端中执行命令看日志进行 Debug。这种低效率(Low)的方式是你想要的吗?显然不是。 8 | 9 | 我们只需要把`.dylib`动态库变成`可执行文件`就能打断点 debug。[LibTooling](https://clang.llvm.org/docs/LibTooling.html) 或许是一个不错的选择。使用 LibTooling 的话,我们只需要改动很少部分的代码就可以。 10 | 11 | **LibTooling 简介**: 12 | 13 | LibTooling 是一个独立的库,它允许使用者很方便地搭建属于你自己的编译器前端工具,它基于 C++ 接口,提供给使用者强大全面的 AST 解析和控制能力,同时由于它与 Clang 的内核过于接近导致它的版本兼容能力比 libclang 差得多,Clang 的变动很容易影响到 LibTooling。libTooling 还提供了完整的参数解析方案,可以很方便的构建一个独立的命令行工具。 14 | 15 | ### 创建 LibTooling 项目及代码调整 16 | 17 | 为了方便,我们直接创建一个可执行的`LibTooling`项目,我们可以创建一个名为`QTPluginTooling`的项目。 18 | 19 | 1. 创建过程跟 [创建插件](https://github.com/CYBoys/Blogs/blob/master/LLVM_Clang/LLVM%20%26%20Clang%20%E5%85%A5%E9%97%A8.md#create_plugin) 步骤差不多,前面 3 步都是一样的,只需要把`QTPlugin`替换为`QTPluginTooling`就可以。 20 | 21 | 2. 只是在第 4 步略有不同,`QTPluginTooling`目录下的`CMakeLists.txt`的文件内容为 22 | 23 | ``` 24 | set(LLVM_LINK_COMPONENTS 25 | Support 26 | ) 27 | 28 | add_clang_executable(QTPluginTooling 29 | QTPluginTooling.cpp 30 | ) 31 | 32 | target_link_libraries(QTPluginTooling 33 | PRIVATE 34 | clangAST 35 | clangBasic 36 | clangDriver 37 | clangFormat 38 | clangLex 39 | clangParse 40 | clangSema 41 | clangFrontend 42 | clangTooling 43 | clangToolingCore 44 | clangRewrite 45 | clangRewriteFrontend 46 | ) 47 | 48 | if (UNIX) 49 | set(CLANGXX__LING_OR_COPY create_symlink) 50 | else() 51 | set(CLANGXX_LINK_OR_COPY copy) 52 | endif() 53 | ``` 54 | 55 | ![tooling_cmakeLists](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/clang_tooling_cmakelists.png) 56 | 57 | 3. 在`llvm_xcode`目录下执行`$ cmake -G Xcode ../llvm`,重新生成一下`Xcode`项目。`Tooling`项目在 Xcode 的`Clang executables`目录下可以找到。 58 | 59 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/clang_executables_tooling.png) 60 | 61 | 4. 将之前 [Plugin](https://github.com/CYBoys/Blogs/blob/master/LLVM_Clang/Examples/QTPlugin/QTPlugin.cpp) 的代码复制过来,新增三个头文件 62 | 63 | ``` 64 | #include "clang/Tooling/CommonOptionsParser.h" 65 | #include "clang/Frontend/FrontendActions.h" 66 | #include "clang/Tooling/Tooling.h" 67 | ``` 68 | 69 | 5. 新增一个命名空间 70 | 71 | ``` 72 | using namespace clang::tooling; 73 | ``` 74 | 75 | 6. 将`QTASTAction`的继承改为继承至`ASTFrontendAction`。 76 | 77 | 7. 将`FrontendPluginRegistry`注册插件的方式注释。更改为`main()`函数方式 78 | 79 | ``` 80 | static llvm::cl::OptionCategory OptsCategory("QTPlugin"); 81 | int main(int argc, const char **argv) { 82 | CommonOptionsParser op(argc, argv, OptsCategory); 83 | ClangTool Tool(op.getCompilations(), op.getSourcePathList()); 84 | return Tool.run(newFrontendActionFactory().get()); 85 | } 86 | ``` 87 | 88 | **最后整个文件的内容可以在 [QTPluginTooling.cpp](https://github.com/CYBoys/Blogs/blob/master/LLVM_Clang/Examples/QTPluginTooling/QTPluginTooling.cpp) 看到。** 89 | 90 | 91 | ### 输入源 92 | 93 | 如果这时候就`run`的话则会直接退出。这是因为没有“输入源”。我们可以在`QTPluginTooling`的`Scheme`加入。 94 | 95 | ``` 96 | /Users/laiyoung_/Desktop/Plugin/ViewController.m 97 | -- 98 | -isysroot 99 | /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk 100 | -isystem 101 | -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/10.0.0/include 102 | -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 103 | -I/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include 104 | -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks 105 | ``` 106 | 107 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/clang_tooling_xcode_schene_arguments.png) 108 | 109 | **参数解释**: 110 | 111 | > 上面在`--`后面的参数,是传递给`CI`的`Compilation DataBase`的,而不是这个命令行工具本身的。比如我们的`ViewController.m`,因为有`#import `这么一条语句,以及继承了`UIViewController`,那么语法分析器(Sema)读到这里的时候就需要知道`UIViewController`的定义是从哪里来的,换句话说就是它需要找到定义`UIViewController`的地方。怎么找呢?通过指定的`-I`、`-F`这些参数指定的目录来寻找。`--`后面的参数,可以理解为如果你要编译`ViewController.m`需要什么参数,那么这个后面就要传递什么参数给我们的 `QTPlugin`,否则就会看到`Console`里打出找不到`xxx`定义或者`xxx.h`文件的错误。当然因为一般的编译指令,会有`-c`参数指定源文件,但是`--`后面并不需要,因为我们在`--`前面就指定了。`--`这种传参的方式还有另外一种方法,使用`-extra-arg="xxxx"`的方式指定编译参数,这样就不需要`--`了。 112 | 113 | ``` 114 | -extra-arg="-Ixxxxxx" 115 | -extra-arg="-Fxxxxxx" 116 | -extra-arg="-isysroot xxxxxx" 117 | xxxxxx表示的路径 118 | ``` 119 | 120 | #### 最终效果: 121 | 122 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/clang_tooling_debuging.png) 123 | 124 | **参考文章**: 125 | 126 | * [打造基于Clang LibTooling的iOS自动打点系统CLAS(二)](https://www.jianshu.com/p/01c988cae897) 127 | * [Compilation databases for Clang-based tools](https://eli.thegreenplace.net/2014/05/21/compilation-databases-for-clang-based-tools) 128 | 129 | 如有内容错误,欢迎 [issue](https://github.com/CYBoys/Blogs/issues/new) 指正。 130 | 131 | **[Example](https://github.com/CYBoys/Blogs/tree/master/LLVM_Clang/Examples/QTPluginTooling)** 132 | 133 | **转载请注明出处!** 134 | 135 | -------------------------------------------------------------------------------- /LLVM_Clang/Examples/QTPlugin/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_llvm_library(QTPlugin MODULE QTPlugin.cpp PLUGIN_TOOL clang) 2 | 3 | if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN)) 4 | target_link_libraries(QTPlugin PRIVATE 5 | clangAST 6 | clangBasic 7 | clangFrontend 8 | LLVMSupport 9 | ) 10 | endif() 11 | -------------------------------------------------------------------------------- /LLVM_Clang/Examples/QTPlugin/QTPlugin.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "clang/AST/AST.h" 3 | #include "clang/AST/DeclObjC.h" 4 | #include "clang/AST/ASTConsumer.h" 5 | #include "clang/ASTMatchers/ASTMatchers.h" 6 | #include "clang/Frontend/CompilerInstance.h" 7 | #include "clang/ASTMatchers/ASTMatchFinder.h" 8 | #include "clang/Frontend/FrontendPluginRegistry.h" 9 | 10 | using namespace clang; 11 | using namespace std; 12 | using namespace llvm; 13 | using namespace clang::ast_matchers; 14 | 15 | namespace QTPlugin { 16 | 17 | class QTMatchHandler: public MatchFinder::MatchCallback { 18 | private: 19 | CompilerInstance &CI; 20 | 21 | bool isUserSourceCode(const string filename) { 22 | if (filename.empty()) return false; 23 | 24 | // 非Xcode中的源码都认为是用户源码 25 | if (filename.find("/Applications/Xcode.app/") == 0) return false; 26 | 27 | return true; 28 | } 29 | 30 | bool isShouldUseCopy(const string typeStr) { 31 | if (typeStr.find("NSString") != string::npos || 32 | typeStr.find("NSArray") != string::npos || 33 | typeStr.find("NSDictionary") != string::npos/*...*/) { 34 | return true; 35 | } 36 | return false; 37 | } 38 | public: 39 | QTMatchHandler(CompilerInstance &CI) :CI(CI) {} 40 | 41 | void run(const MatchFinder::MatchResult &Result) { 42 | const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs("objcPropertyDecl"); 43 | if (propertyDecl && isUserSourceCode(CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str()) ) { 44 | ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes(); 45 | string typeStr = propertyDecl->getType().getAsString(); 46 | 47 | if (propertyDecl->getTypeSourceInfo() && isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) { 48 | cout<<"--------- "<getBeginLoc(), diag.getCustomDiagID(DiagnosticsEngine::Warning, "--------- %0 不是使用的 copy 修饰--------")) << typeStr; 51 | } 52 | } 53 | } 54 | }; 55 | 56 | class QTASTConsumer: public ASTConsumer { 57 | private: 58 | MatchFinder matcher; 59 | QTMatchHandler handler; 60 | public: 61 | QTASTConsumer(CompilerInstance &CI) :handler(CI) { 62 | matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &handler); 63 | } 64 | 65 | void HandleTranslationUnit(ASTContext &context) { 66 | matcher.matchAST(context); 67 | } 68 | }; 69 | 70 | class QTASTAction: public PluginASTAction { 71 | public: 72 | unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef iFile) { 73 | return unique_ptr (new QTASTConsumer(CI)); 74 | } 75 | 76 | bool ParseArgs(const CompilerInstance &ci, const std::vector &args) { 77 | return true; 78 | } 79 | }; 80 | } 81 | 82 | static FrontendPluginRegistry::Add X("QTPlugin", "The QTPlugin is my first clang-plugin."); 83 | -------------------------------------------------------------------------------- /LLVM_Clang/Examples/QTPluginTooling/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LLVM_LINK_COMPONENTS 2 | Support 3 | ) 4 | 5 | add_clang_executable(QTPluginTooling 6 | QTPluginTooling.cpp 7 | ) 8 | 9 | target_link_libraries(QTPluginTooling 10 | PRIVATE 11 | clangAST 12 | clangBasic 13 | clangDriver 14 | clangFormat 15 | clangLex 16 | clangParse 17 | clangSema 18 | clangFrontend 19 | clangTooling 20 | clangToolingCore 21 | clangRewrite 22 | clangRewriteFrontend 23 | ) 24 | 25 | if (UNIX) 26 | set(CLANGXX__LING_OR_COPY create_symlink) 27 | else() 28 | set(CLANGXX_LINK_OR_COPY copy) 29 | endif() 30 | -------------------------------------------------------------------------------- /LLVM_Clang/Examples/QTPluginTooling/QTPluginTooling.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "clang/AST/AST.h" 3 | #include "clang/AST/DeclObjC.h" 4 | #include "clang/AST/ASTConsumer.h" 5 | #include "clang/ASTMatchers/ASTMatchers.h" 6 | #include "clang/Frontend/CompilerInstance.h" 7 | #include "clang/ASTMatchers/ASTMatchFinder.h" 8 | #include "clang/Frontend/FrontendPluginRegistry.h" 9 | #include "clang/Tooling/CommonOptionsParser.h" 10 | #include "clang/Frontend/FrontendActions.h" 11 | #include "clang/Tooling/Tooling.h" 12 | 13 | using namespace clang; 14 | using namespace std; 15 | using namespace llvm; 16 | using namespace clang::ast_matchers; 17 | using namespace clang::tooling; 18 | 19 | namespace QTPlugin { 20 | 21 | class QTMatchHandler: public MatchFinder::MatchCallback { 22 | private: 23 | CompilerInstance &CI; 24 | 25 | bool isUserSourceCode(const string filename) { 26 | if (filename.empty()) return false; 27 | 28 | // 非Xcode中的源码都认为是用户源码 29 | if (filename.find("/Applications/Xcode.app/") == 0) return false; 30 | 31 | return true; 32 | } 33 | 34 | bool isShouldUseCopy(const string typeStr) { 35 | if (typeStr.find("NSString") != string::npos || 36 | typeStr.find("NSArray") != string::npos || 37 | typeStr.find("NSDictionary") != string::npos/*...*/) { 38 | return true; 39 | } 40 | return false; 41 | } 42 | public: 43 | QTMatchHandler(CompilerInstance &CI) :CI(CI) {} 44 | 45 | void run(const MatchFinder::MatchResult &Result) { 46 | const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs("objcPropertyDecl"); 47 | if (propertyDecl && isUserSourceCode(CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str()) ) { 48 | ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes(); 49 | string typeStr = propertyDecl->getType().getAsString(); 50 | 51 | if (propertyDecl->getTypeSourceInfo() && isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) { 52 | cout<<"--------- "<getBeginLoc(), diag.getCustomDiagID(DiagnosticsEngine::Warning, "--------- %0 不是使用的 copy 修饰--------")) << typeStr; 55 | } 56 | } 57 | } 58 | }; 59 | 60 | class QTASTConsumer: public ASTConsumer { 61 | private: 62 | MatchFinder matcher; 63 | QTMatchHandler handler; 64 | public: 65 | QTASTConsumer(CompilerInstance &CI) :handler(CI) { 66 | matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &handler); 67 | } 68 | 69 | void HandleTranslationUnit(ASTContext &context) { 70 | matcher.matchAST(context); 71 | } 72 | }; 73 | 74 | class QTASTAction: public ASTFrontendAction { 75 | public: 76 | unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef iFile) { 77 | return unique_ptr (new QTASTConsumer(CI)); 78 | } 79 | 80 | bool ParseArgs(const CompilerInstance &ci, const std::vector &args) { 81 | return true; 82 | } 83 | }; 84 | } 85 | 86 | //static FrontendPluginRegistry::Add X("QTPlugin", "The QTPlugin is my first clang-plugin."); 87 | 88 | static llvm::cl::OptionCategory OptsCategory("QTPlugin"); 89 | int main(int argc, const char **argv) { 90 | CommonOptionsParser op(argc, argv, OptsCategory); 91 | ClangTool Tool(op.getCompilations(), op.getSourcePathList()); 92 | return Tool.run(newFrontendActionFactory().get()); 93 | } 94 | -------------------------------------------------------------------------------- /LLVM_Clang/LLVM & Clang 入门.md: -------------------------------------------------------------------------------- 1 | # LLVM & Clang 入门 2 | 3 | 本文主要从下面几个方面简单介绍了一下 LLVM & Clang。 4 | 5 | [概述](#overview) 6 | 7 | [快速入门](#quick_start) 8 | 9 | [Clang 三大件](#clang_three) 10 | 11 | [Xcode 编译过程](#xcode_compile_process) 12 | 13 | [创建插件](#create_plugin) 14 | 15 | [编写插件(实战)](#code_plugin) 16 | 17 | [Xcode 集成 Plugin](#xcode_integrated_plugin) 18 | 19 | ## 概述 20 | 21 | `LLVM`包含三部分,分别是`LLVM suite`、`Clang`和`Test Suite`。 22 | 23 | 1. `LLVM suite`,LLVM 套件,它包含了 LLVM 所需要的所有工具、库和头文件,一个汇编器、解释器、位码分析器和位码优化器,还包含了可用于测试 LLVM 的工具和 clang 前端的基本回归测试。 24 | 25 | 2. `Clang`,俗称为 Clang 前端,该组件将`C`,`C++`,`Objective C`,和 `Objective C++`代码编译到 LLVM 的位码中。一旦编译到 LLVM 位代码中,就可以使用 LLVM 套件中的工具来操作程序。 26 | 27 | 3. `Test Suite`,测试套件,这是一个可选的工具,它是一套带有测试工具的程序,可用于进一步测试 LLVM 的功能和性能。 28 | 29 | ## 快速入门 30 | 31 | 官方建议查看 Clang 的[入门文档](http://clang.llvm.org/get_started.html),因为 LLVM 的文档可能已经过期。 32 | 33 | #### Checkout LLVM: 34 | 35 | * `$ cd 到放 LLVM 的路径下` 36 | 37 | * `$ git clone https://git.llvm.org/git/llvm.git/` 38 | 39 | #### Checkout Clang: 40 | 41 | * `$ cd llvm/tools` 42 | 43 | * `$ git clone https://git.llvm.org/git/clang.git/` 44 | 45 | #### 配置和构建 LLVM 和 Clang: 46 | 47 | 这里有`Xcode`和`ninja`两种编译方式。 48 | 49 | 需要使用到的编译工具是[`CMake`](https://llvm.org/docs/CMake.html),`CMake`的最低版本要求为`3.4.3`,不了解`CMake`的同学可以[戳我](http://www.hahack.com/codes/cmake/)进行入门了解。 50 | 安装`CMake`需要用到[`brew`](https://brew.sh/),请确认`brew`已经安装。 51 | 使用`$ brew install cmake`命令即可安装`CMake`。 52 | 53 | ##### 方式一:使用 ninja 进行编译 54 | 55 | 使用`ninja`进行编译则还需要安装[`ninja`](https://ninja-build.org/)。 56 | 使用`$ brew install ninja`命令即可安装`ninja`。 57 | 58 | 1. 在`llvm`源码根目录下新建一个`llvm_build`目录,最终会在`llvm_build`目录下生成`build.ninja`。 59 | 60 | 2. 在`llvm`源码根目录下新建一个`llvm_release`目录,最终编译文件会在`llvm_release`文件夹路径下。 61 | 62 | * `$ cd llvm_build` 63 | 64 | * `$ cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX= 安装路径(本机为/Users/xxx/xxx/LLVM/llvm_release`,注意`DCMAKE_INSTALL_PREFIX`后面不能有空格。 65 | 66 | 3. 依次执行编译、安装指令。 67 | 68 | * `$ ninja` 69 | 70 | * `$ ninja install` 71 | 72 | ##### 方式二:使用 Xcode 进行编译 73 | 74 | 1. 在`llvm`源码根目录的同级下创建一个名为`llvm_xcode`的目录,并`$cd llvm_xcode`进入到`llvm_xcode`。 75 | 76 | 2. 编译命令:`cmake -G [options] ` 77 | 78 | **generator commands**: 79 | 80 | * `Unix Makefiles` — 生成和 make 兼容的并行的 makefile。 81 | 82 | * `Ninja` — 生成一个 Ninja 编译文件,大多数 LLVM 开发者使用 Ninja。 83 | 84 | * `Visual Studio` — 生成一个 Visual Studio 项目。 85 | 86 | * `Xcode` — 生成一个 Xcode 项目。 87 | 88 | **options commands** 89 | 90 | * `-DCMAKE_INSTALL_PREFIX=`"directory" — 安装 LLVM 工具和库的完整路径,默认`/usr/local`。 91 | 92 | * `-DCMAKE_BUILD_TYPE=`"type" — type 的值为`Debug`,`Release`, `RelWithDebInfo`和`MinSizeRel`,默认`Debug`。 93 | 94 | * `-DLLVM_ENABLE_ASSERTIONS=`"On" — 在启用断言检查的情况下编译,默认为`Yes`。 95 | 96 | 3. 这里我们使用`$ cmake -G Xcode ../llvm`命令生成一个`Xcode`项目。 97 | 98 | 4. 编译,选择`ALL_BUILD` Secheme 进行编译,预计`1+`小时。 99 | 100 | ![All_BUILD](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/llvm_clang_all_build.png) 101 | 102 | ## Clang 三大件 103 | 104 | Clang 三大件分别是[`LibClang`](https://clang.llvm.org/doxygen/group__CINDEX.html)、[`Clang Plugins`](https://clang.llvm.org/docs/ClangPlugins.html)和[`LibTooling`](https://clang.llvm.org/docs/LibTooling.html)。 105 | 106 | #### LibClang: 107 | 108 | libclang 供了一个相对较小的 API,它将用于解析源代码的工具暴露给抽象语法树(AST),加载已经解析的 AST,遍历 AST,将物理源位置与 AST 内的元素相关联。 109 | 110 | libclang 是一个稳定的高级 C 语言接口,隔离了编译器底层的复杂设计,拥有更强的 Clang 版本兼容性,以及更好的多语言支持能力,对于大多数分析 AST 的场景来说,libclang 是一个很好入手的选择。 111 | 112 | ##### 优点 113 | 114 | 1. 可以使用 C++ 之外的语言与 Clang 交互。 115 | 2. 稳定的交互接口和向后兼容。 116 | 3. 强大的高级抽象,比如用光标迭代 AST,并且不用学习 Clang AST 的所有细节。 117 | 118 | ##### 缺点 119 | 120 | 1. 不能完全控制 Clang AST。 121 | 122 | #### Clang Plugins: 123 | 124 | Clang Plugin 允许你在编译过程中对 AST 执行其他操作。Clang Plugin 是动态库,由编译器在运行时加载,并且它们很容易集成到构建环境中。 125 | 126 | #### LibTooling: 127 | 128 | LibTooling 是一个独立的库,它允许使用者很方便地搭建属于你自己的编译器前端工具,它的优点与缺点一样明显,它基于 C++ 接口,读起来晦涩难懂,但是提供给使用者远比 libclang 强大全面的 AST 解析和控制能力,同时由于它与 Clang 的内核过于接近导致它的版本兼容能力比 libclang 差得多,Clang 的变动很容易影响到 LibTooling。libTooling 还提供了完整的参数解析方案,可以很方便的构建一个独立的命令行工具。这是 libclang 所不具备的能力。一般来说,如果你只需要语法分析或者做代码补全这类功能,libclang 将是你避免掉坑的最佳的选择。 129 | 130 | ## Xcode 编译过程 131 | 132 | ![LLVM](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_compiler_process.jpeg) 133 | 134 | `Objective-C`与`swift`都采用`Clang`作为编译器前端,编译器前端主要进行语法分析、语义分析、生成中间代码,在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。 135 | 136 | ![LLVM](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_compiler_process_3.png) 137 | 138 | 编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化,根据不同的系统架构生成不同的机器码。 139 | 140 | `C++`,`Objective-C`都是编译语言。编译语言在执行的时候,必须先通过编译器生成机器码。 141 | 142 | ![LLVM](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_compiler_process_2.png) 143 | 144 | 如上图所示,在`Xcode`按下`CMD+B`之后的工作流程。 145 | 146 | * **预处理(Pre-process)**:他的主要工作就是将宏替换,删除注释展开头文件,生成`.i`文件。 147 | 148 | * **词法分析(Lexical Analysis)**:将代码切成一个个 token,比如大小括号,等于号还有字符串等。是计算机科学中将字符序列转换为标记序列的过程。 149 | 150 | * **语法分析(Semantic Analysis)**:验证语法是否正确,然后将所有节点组成抽象语法树 AST 。由 Clang 中 Parser 和 Sema 配合完成。 151 | 152 | * **静态分析(Static Analysis)**:使用它来表示用于分析源代码以便自动发现错误。 153 | 154 | * **中间代码生成(Code Generation)**:生成中间代码 IR,CodeGen 会负责将语法树自顶向下遍历逐步翻译成 LLVM IR,IR 是编译过程的前端的输出,后端的输入。 155 | 156 | * **优化(Optimize)**:LLVM 会去做些优化工作,在 Xcode 的编译设置里也可以设置优化级别`-O1`、`-O3`、`-Os`...还可以写些自己的 Pass,官方有比较完整的 Pass 教程: [Writing an LLVM Pass](http://llvm.org/docs/WritingAnLLVMPass.html) 。如果开启了`Bitcode`苹果会做进一步的优化,有新的后端架构还是可以用这份优化过的`Bitcode`去生成。 157 | 158 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_optimize.png) 159 | 160 | * **生成目标文件(Assemble)**:生成`Target`相关`Object`(Mach-o)。 161 | 162 | * **链接(Link)**:生成`Executable`可执行文件。 163 | 164 | 经过这一步步,我们用各种高级语言编写的代码就转换成了机器可以看懂可以执行的目标代码了。 165 | 166 | 这里只是作了一个`Xcode`编译过程的一个简单的介绍,需要深入了解的同学可以查看 [深入浅出iOS编译](https://github.com/LeoMobileDeveloper/Blogs/blob/master/Compiler/xcode-compile-deep.md) 。 167 | 168 | ## 创建插件 169 | 170 | 1. 在`/llvm/tools/clang/tools`目录下新建插件。 171 | 172 | ![create clang plugin](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/create_clang_plugin_0.png) 173 | 174 | 2. 修改`/llvm/tools/clang/tools`目录下的`CMakeLists.txt`文件,新增`add_clang_subdirectory(xxPlugin)`。 175 | 176 | ![create clang plugin](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/create_clang_plugin_1.png) 177 | 178 | 3. 在`QTPlugin`目录下新建一个名为`xxPlugin.cpp`的文件。 179 | 180 | 4. 在`QTPlugin`目录下新建一个名为`CMakeLists.txt`的文件,内容为 181 | 182 | ``` 183 | add_llvm_library(xxPlugin MODULE xxPlugin.cpp PLUGIN_TOOL clang) 184 | 185 | if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN)) 186 | target_link_libraries(xxPlugin PRIVATE 187 | clangAST 188 | clangBasic 189 | clangFrontend 190 | LLVMSupport 191 | ) 192 | endif() 193 | ``` 194 | 195 | 有可能会随着版本的变化导致上面的内容在编译的时候使用`cmake`命令会编译不通过。建议参照`LLVM.xcodeproj`工程下的`Loadable modules`里面的`CMakeLists.txt`内容进行编写。 196 | 197 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/create_clang_plugin_2.png) 198 | 199 | 5. 目录文件创建完成之后,利用`cmake`重新生成一下`Xcode`项目。在`llvm_xcode`目录下执行`$ cmake -G Xcode ../llvm`。 200 | 201 | 6. 插件源代码在 Xcode 项目中的`Loadable modules`目录下可以找到,这样就可以直接在 Xcode 里编写插件代码。 202 | 203 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_clang_plugin_code.png) 204 | 205 | ## 编写插件(实战) 206 | 207 | **宗旨**:重载`Clang`编译过程的函数,实现自定义需求(分析),大多数情况都是对源代码分析。 208 | 209 | #### 插件文件(.cpp)结构(组成) 210 | 211 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/code_clang_plugin.png) 212 | 213 | 上图是`Clang Plugin`执行的过程,分别有[`CompilerInstance`](https://clang.llvm.org/doxygen/classclang_1_1CompilerInstance.html#ad0d4578fb5e22cfe0f831024b88dc48c)、[`FrontendAction`](https://clang.llvm.org/doxygen/classclang_1_1FrontendAction.html#ac3d51f3f03d11bbe9355cf91708aa156)和[`ASTConsumer`](https://clang.llvm.org/doxygen/classclang_1_1ASTConsumer.html#abb9a2e25f40387eea6c9bbf534031bb3)。 214 | 215 | **CompilerInstance**:是一个编译器实例,综合了一个 Compiler 需要的 objects,如 Preprocessor,ASTContext(真正保存 AST 内容的类),DiagnosticsEngine,TargetInfo 等。 216 | 217 | **FrontendAction**:是一个基于 Consumer 的抽象语法树(Abstract Syntax Tree/AST)前端 Action 抽象基类,对于 Plugin,我们可以继承至系统专门提供的[`PluginASTAction`](https://clang.llvm.org/doxygen/classclang_1_1PluginASTAction.html#a738fc8000ed0a254d23fb44f0fd1d54c)来实现我们自定义的 Action,我们重载`CreateASTConsumer()`函数返回自定义的`Consumer`,来读取 AST Nodes。 218 | 219 | ``` 220 | unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 221 | return unique_ptr (new QTASTConsumer); 222 | } 223 | ``` 224 | 225 | **ASTConsumer**:是一个读取抽象语法树的抽象基类,我们可以重载下面两个函数: 226 | 227 | * `HandleTopLevelDecl()`:解析顶级的声明(像全局变量,函数定义等)的时候被调用。 228 | 229 | * `HandleTranslationUnit()`:在整个文件都解析完后会被调用。 230 | 231 | 除了上面提到的这几个类,还有两个比较重要的类,分别是[`RecursiveASTVisitor`](http://clang.llvm.org/docs/RAVFrontendAction.html)和[`MatchFinder`](http://clang.llvm.org/docs/LibASTMatchersReference.html)。 232 | 233 | **RecursiveASTVisitor**:是一个特别有用的类,使用它可以访问任意类型的 AST 节点。 234 | 235 | * `VisitStmt()`:分析表达式。 236 | 237 | * `VisitDecl()`:分析所有声明。 238 | 239 | **MatchFinder**:是一个 AST 节点的查找过滤匹配器,可以使用`addMatcher`函数去匹配自己关注的 AST 节点。 240 | 241 | **基础结构如👇所示**:其中的`QTASTVisitor`不是必须的,如果你不需要访问 AST 节点,则可以根据自己对应的业务场景进行调整,这里只是举例!!!。 242 | 243 | ``` 244 | #include 245 | #include "clang/AST/AST.h" 246 | #include "clang/AST/ASTConsumer.h" 247 | #include "clang/AST/RecursiveASTVisitor.h" 248 | #include "clang/Frontend/CompilerInstance.h" 249 | #include "clang/Frontend/FrontendPluginRegistry.h" 250 | 251 | using namespace clang; 252 | using namespace std; 253 | using namespace llvm; 254 | 255 | namespace QTPlugin { 256 | 257 | // ...other 258 | 259 | class QTASTVisitor : public RecursiveASTVisitor { 260 | private: 261 | ASTContext *context; 262 | public: 263 | void setContext(ASTContext &context) { 264 | this->context = &context; 265 | } 266 | // 分析所有声明 267 | bool VisitDecl(Decl *decl) { 268 | return true;// 返回true以继续遍历AST,返回false以终止遍历,退出Clang 269 | } 270 | // 分析表达式 271 | bool VisitStmt(Stmt *S) { 272 | return true;// 返回true以继续遍历AST,返回false以终止遍历,退出Clang 273 | } 274 | }; 275 | 276 | class QTASTConsumer: public ASTConsumer { 277 | private: 278 | QTASTVisitor visitor; 279 | // 解析完顶级的声明(像全局变量,函数定义等)后被调用 280 | bool HandleTopLevelDecl(DeclGroupRef D) { 281 | return true; 282 | } 283 | // 在整个文件都解析完后被调用 284 | void HandleTranslationUnit(ASTContext &context) { 285 | visitor.setContext(context); 286 | visitor.TraverseDecl(context.getTranslationUnitDecl()); 287 | } 288 | }; 289 | 290 | class QTASTAction: public PluginASTAction { 291 | public: 292 | unique_ptr CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 293 | return unique_ptr (new QTASTConsumer); 294 | } 295 | bool ParseArgs(const CompilerInstance &CI, const std::vector < std::string >& args) { 296 | return true; 297 | } 298 | }; 299 | } 300 | 301 | // 注册插件 302 | static clang::FrontendPluginRegistry::Add < QTPlugin::QTASTAction > X("QTPlugin", "QTPlugin desc"); 303 | ``` 304 | 305 | #### 如何编写插件相关代码? 306 | 307 | 对源代码(自己写的)进行代码分析的,比如`Objc`的`property`修饰关键字,我们就可以使用 clang 命令,打印出所有的 AST Nodes 来进行分析。 308 | 我们的源文件内容如下: 309 | 310 | ``` 311 | #import 312 | 313 | @interface ViewController : UIViewController 314 | 315 | @property (nonatomic, strong) NSString *string; 316 | @property (nonatomic, strong) NSArray *array; 317 | 318 | @end 319 | 320 | @implementation ViewController 321 | @end 322 | ``` 323 | 324 | 会发现`NSString`和`NSArray`我们都使用了`strong`进行修饰。 325 | 326 | 使用`clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk -fmodules -fsyntax-only -Xclang -ast-dump `命令,打印出所有的 AST Nodes 如下图。 327 | 328 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/clang_AST.png) 329 | 330 | 会发现在圈中的内容中`ObjCPropertyDecl`,表示的是一个`Objc`类的属性声明。其中包含了类名、变量名以及修饰关键字。 331 | 我们可以使用`MatchFinder`匹配`ObjCPropertyDecl`节点。 332 | 333 | ``` 334 | class QTASTConsumer: public ASTConsumer { 335 | private: 336 | MatchFinder matcher; 337 | QTMatchHandler handler; 338 | public: 339 | QTASTConsumer(CompilerInstance &CI) :handler(CI) { 340 | matcher.addMatcher(objcPropertyDecl().bind("objcPropertyDecl"), &handler); 341 | } 342 | 343 | void HandleTranslationUnit(ASTContext &context) { 344 | matcher.matchAST(context); 345 | } 346 | }; 347 | ``` 348 | 349 | 这里的`QTMatchHandler`是我们继承至的`MatchFinder::MatchCallback`的一个类,我们可以在`run()`函数里面去判断哪些应该使用`copy`关键字修饰的,而没有使用 copy 修饰的 property。 350 | 351 | ``` 352 | class QTMatchHandler: public MatchFinder::MatchCallback { 353 | private: 354 | CompilerInstance &CI; 355 | 356 | bool isUserSourceCode(const string filename) { 357 | if (filename.empty()) return false; 358 | 359 | // 非Xcode中的源码都认为是用户源码 360 | if (filename.find("/Applications/Xcode.app/") == 0) return false; 361 | 362 | return true; 363 | } 364 | 365 | bool isShouldUseCopy(const string typeStr) { 366 | if (typeStr.find("NSString") != string::npos || 367 | typeStr.find("NSArray") != string::npos || 368 | typeStr.find("NSDictionary") != string::npos/*...*/) { 369 | return true; 370 | } 371 | return false; 372 | } 373 | public: 374 | QTMatchHandler(CompilerInstance &CI) :CI(CI) {} 375 | 376 | void run(const MatchFinder::MatchResult &Result) { 377 | const ObjCPropertyDecl *propertyDecl = Result.Nodes.getNodeAs("objcPropertyDecl"); 378 | if (propertyDecl && isUserSourceCode(CI.getSourceManager().getFilename(propertyDecl->getSourceRange().getBegin()).str()) ) { 379 | ObjCPropertyDecl::PropertyAttributeKind attrKind = propertyDecl->getPropertyAttributes(); 380 | string typeStr = propertyDecl->getType().getAsString(); 381 | 382 | if (propertyDecl->getTypeSourceInfo() && isShouldUseCopy(typeStr) && !(attrKind & ObjCPropertyDecl::OBJC_PR_copy)) { 383 | cout<<"--------- "<getBeginLoc(), diag.getCustomDiagID(DiagnosticsEngine::Warning, "--------- %0 不是使用的 copy 修饰--------")) << typeStr; 386 | } 387 | } 388 | } 389 | }; 390 | ``` 391 | 392 | **最后整个文件的内容可以在 [QTPlugin.cpp](https://github.com/CYBoys/Blogs/blob/master/LLVM_Clang/Examples/QTPlugin/QTPlugin.cpp) 看到。** 393 | 394 | 最后`CMD+B`编译生成`.dylib`文件,找到插件对应的`.dylib`,右键`show in finder`。 395 | 396 | **验证**:我们可以在终端中使用命令的方式进行验证 397 | 398 | ``` 399 | 自己编译的clang文件路径 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk/ -Xclang -load -Xclang 插件(.dylib)路径 -Xclang -add-plugin -Xclang 插件名 -c 资源文件(.h或者.m) 400 | ``` 401 | 402 | 举一个🌰,我当前是在`ViewController.m`目录下。 403 | 404 | ``` 405 | /Users/laiyoung_/Documents/LLVM/llvm_xcode/Debug/bin/clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk/ -Xclang -load -Xclang /Users/laiyoung_/Documents/LLVM/llvm_xcode/Debug/lib/QTPropertyCheckPlugin.dylib -Xclang -add-plugin -Xclang QTPlugin -c ./ViewController.m 406 | ``` 407 | 408 | 输出结果: 409 | 410 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/terminal_console_log.png) 411 | 412 | ## Xcode 集成 Plugin 413 | 414 | #### 加载插件: 415 | 416 | 打开需要加载插件的`Xcode`项目,在`Build Settings`栏目中的`OTHER_CFLAGS`添加上如下内容: 417 | 418 | ``` 419 | -Xclang -load -Xclang (.dylib)动态库路径 -Xclang -add-plugin -Xclang 插件名字(namespace 的名字,名字不对则无法使用插件) 420 | ``` 421 | 422 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_other_cflags.png) 423 | 424 | #### 设置编译器: 425 | 426 | 由于`Clang`插件需要使用对应的版本去加载,如果版本不一致则会导致编译错误,会出现如下图所示: 427 | 428 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_compiler_error.png) 429 | 430 | 在`Build Settings`栏目中新增两项用户定义的设置 431 | 432 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_add_user_defined_settings.png) 433 | 434 | 分别是`CC`和`CXX`。 435 | 436 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_added_user_defined_cc_cxx.png) 437 | 438 | `CC`对应的是自己编译的`clang`的绝对路径,`CXX`对应的是自己编译的`clang++`的绝对路径。 439 | 440 | 如果👆的步骤都确认无误之后,在编译的时候如果遇到了下图这种错误 441 | 442 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_index_store_path_error.png) 443 | 444 | 则可以在`Build Settings`栏目中搜索`index`,将`Enable Index-Wihle-Building Functionality`的`Default`改为`NO`。 445 | 446 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_enable_index_wihle_building_functionality_no.png) 447 | 448 | #### 最终效果: 449 | 450 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/xcode_load_plugin_result.png) 451 | 452 | **参考文章**: 453 | 454 | * [Clang Plugin 之 Debug](https://github.com/CYBoys/Blogs/blob/master/LLVM_Clang/Clang%20Plugin%20%E4%B9%8B%20Debug.md) 455 | * [Clang 之旅--实现一个自定义检查规范的 Clang 插件](https://www.jianshu.com/p/c27b77f70616) 456 | * [基于LLVM开发属于自己Xcode的Clang插件](https://www.jianshu.com/p/4935e919bb45) 457 | * [Clang Tutorial 第二部分(LibTooling)](http://jszhujun2010.farbox.com/post/llvm&clang/clang-tutorial-di-er-bu-fen) 458 | * [Clang Tutorial 第三部分(Plugin)](http://jszhujun2010.farbox.com/post/llvm&clang/clang-tutorial-di-san-bu-fen) 459 | * [Clang之语法抽象语法树AST](http://www.cnblogs.com/zhangke007/p/4714245.html) 460 | * [LLVM与Clang的一些事儿](https://juejin.im/post/5a30ea0ff265da43094526f9) 461 | * [使用Xcode开发iOS语法检查的Clang插件](https://www.jianshu.com/p/581ef614a1c5) 462 | 463 | **推荐文章**: 464 | 465 | * [深入研究Clang](https://zhuanlan.zhihu.com/clang) 466 | * [LLVM每日谈](https://zhuanlan.zhihu.com/llvm-clang) 467 | 468 | 如有内容错误,欢迎 [issue](https://github.com/CYBoys/Blogs/issues/new) 指正。 469 | 470 | **[Example](https://github.com/CYBoys/Blogs/tree/master/LLVM_Clang/Examples/QTPlugin)** 471 | 472 | **转载请注明出处!** 473 | 474 | 475 | -------------------------------------------------------------------------------- /Pic/clang_AST.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/clang_AST.png -------------------------------------------------------------------------------- /Pic/clang_executables_tooling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/clang_executables_tooling.png -------------------------------------------------------------------------------- /Pic/clang_ouput.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/clang_ouput.png -------------------------------------------------------------------------------- /Pic/clang_plugin_diagnostics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/clang_plugin_diagnostics.png -------------------------------------------------------------------------------- /Pic/clang_tooling_cmakelists.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/clang_tooling_cmakelists.png -------------------------------------------------------------------------------- /Pic/clang_tooling_debuging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/clang_tooling_debuging.png -------------------------------------------------------------------------------- /Pic/clang_tooling_xcode_schene_arguments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/clang_tooling_xcode_schene_arguments.png -------------------------------------------------------------------------------- /Pic/code_clang_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/code_clang_plugin.png -------------------------------------------------------------------------------- /Pic/create_clang_plugin_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/create_clang_plugin_0.png -------------------------------------------------------------------------------- /Pic/create_clang_plugin_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/create_clang_plugin_1.png -------------------------------------------------------------------------------- /Pic/create_clang_plugin_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/create_clang_plugin_2.png -------------------------------------------------------------------------------- /Pic/ios/160ed39277c58076.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed39277c58076.png -------------------------------------------------------------------------------- /Pic/ios/160ed39dac1bfea0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed39dac1bfea0.png -------------------------------------------------------------------------------- /Pic/ios/160ed3a2a977adc4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed3a2a977adc4.png -------------------------------------------------------------------------------- /Pic/ios/160ed3bde0a93494.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed3bde0a93494.png -------------------------------------------------------------------------------- /Pic/ios/160ed3d277057ae1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed3d277057ae1.png -------------------------------------------------------------------------------- /Pic/ios/160ed6f6d366332a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed6f6d366332a.png -------------------------------------------------------------------------------- /Pic/ios/160ed734379c2e01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed734379c2e01.png -------------------------------------------------------------------------------- /Pic/ios/160ed7bbe09085cd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed7bbe09085cd.png -------------------------------------------------------------------------------- /Pic/ios/160ed81d2b12988a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed81d2b12988a.png -------------------------------------------------------------------------------- /Pic/ios/160ed8f2edde989c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed8f2edde989c.png -------------------------------------------------------------------------------- /Pic/ios/160ed91244f83b13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ed91244f83b13.png -------------------------------------------------------------------------------- /Pic/ios/160ee2a30718e52a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ee2a30718e52a.png -------------------------------------------------------------------------------- /Pic/ios/160ee2a9d754e46f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ee2a9d754e46f.png -------------------------------------------------------------------------------- /Pic/ios/160ee2f3888ec01e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/160ee2f3888ec01e.png -------------------------------------------------------------------------------- /Pic/ios/161301cd070af847.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/161301cd070af847.png -------------------------------------------------------------------------------- /Pic/ios/16130259c16487a4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/16130259c16487a4.png -------------------------------------------------------------------------------- /Pic/ios/16170be40bdda6f2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/16170be40bdda6f2.png -------------------------------------------------------------------------------- /Pic/ios/16170bef5ace5962.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/16170bef5ace5962.png -------------------------------------------------------------------------------- /Pic/ios/1618448a7403100f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/1618448a7403100f.png -------------------------------------------------------------------------------- /Pic/ios/161c09f5e3469ce4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/161c09f5e3469ce4.png -------------------------------------------------------------------------------- /Pic/ios/1623e39ade981eb6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/1623e39ade981eb6.png -------------------------------------------------------------------------------- /Pic/ios/1624403c83678104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/1624403c83678104.png -------------------------------------------------------------------------------- /Pic/ios/162440e866880e84.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/162440e866880e84.png -------------------------------------------------------------------------------- /Pic/ios/1624430835219f66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/1624430835219f66.png -------------------------------------------------------------------------------- /Pic/ios/1624435303eade8b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/ios/1624435303eade8b.png -------------------------------------------------------------------------------- /Pic/llvm_clang_all_build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/llvm_clang_all_build.png -------------------------------------------------------------------------------- /Pic/terminal_console_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/terminal_console_log.png -------------------------------------------------------------------------------- /Pic/xcode_add_user_defined_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_add_user_defined_settings.png -------------------------------------------------------------------------------- /Pic/xcode_added_user_defined_cc_cxx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_added_user_defined_cc_cxx.png -------------------------------------------------------------------------------- /Pic/xcode_clang_plugin_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_clang_plugin_code.png -------------------------------------------------------------------------------- /Pic/xcode_compiler_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_compiler_error.png -------------------------------------------------------------------------------- /Pic/xcode_compiler_process.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_compiler_process.jpeg -------------------------------------------------------------------------------- /Pic/xcode_compiler_process_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_compiler_process_2.png -------------------------------------------------------------------------------- /Pic/xcode_compiler_process_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_compiler_process_3.png -------------------------------------------------------------------------------- /Pic/xcode_enable_index_wihle_building_functionality_no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_enable_index_wihle_building_functionality_no.png -------------------------------------------------------------------------------- /Pic/xcode_index_store_path_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_index_store_path_error.png -------------------------------------------------------------------------------- /Pic/xcode_load_plugin_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_load_plugin_result.png -------------------------------------------------------------------------------- /Pic/xcode_optimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_optimize.png -------------------------------------------------------------------------------- /Pic/xcode_other_cflags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Pic/xcode_other_cflags.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## LLVM & Clang 2 | 3 | * [LLVM & Clang 入门](https://github.com/CYBoys/Blogs/blob/master/LLVM_Clang/LLVM%20%26%20Clang%20%E5%85%A5%E9%97%A8.md) 4 | 5 | * [Clang Plugin 之 Debug](https://github.com/CYBoys/Blogs/blob/master/LLVM_Clang/Clang%20Plugin%20%E4%B9%8B%20Debug.md) 6 | 7 | ## iOS 8 | 9 | * [iOS 性能优化总结](https://github.com/CYBoys/Blogs/blob/master/iOS/ios-performance-optimization-summary.md) 10 | 11 | * [iOS 持续交付之 Fastlane](https://github.com/CYBoys/Blogs/blob/master/iOS/ios-fastlane.md) 12 | 13 | ## Shader 14 | 15 | * [Shader 之 Blend](https://github.com/CYBoys/Blogs/blob/master/Shader/Shader_Blend.md) 16 | 17 | -------------------------------------------------------------------------------- /Shader/Pic/07EACBD3-9938-43E9-A398-A85E6D3239A6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/07EACBD3-9938-43E9-A398-A85E6D3239A6.jpg -------------------------------------------------------------------------------- /Shader/Pic/0DE4BA16-DDC6-4E09-AA6D-AC575323D8E4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/0DE4BA16-DDC6-4E09-AA6D-AC575323D8E4.png -------------------------------------------------------------------------------- /Shader/Pic/1F0E0949-32F3-4D4B-81AD-AD6B5500D37E.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/1F0E0949-32F3-4D4B-81AD-AD6B5500D37E.jpg -------------------------------------------------------------------------------- /Shader/Pic/2C9EFD01-0E74-4A68-A3C1-DDD2CAB0B019.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/2C9EFD01-0E74-4A68-A3C1-DDD2CAB0B019.png -------------------------------------------------------------------------------- /Shader/Pic/3622A2E4-AE32-4C45-B32E-6444BB868751.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/3622A2E4-AE32-4C45-B32E-6444BB868751.png -------------------------------------------------------------------------------- /Shader/Pic/3C599202-CA10-4292-98F3-F0BD859E793D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/3C599202-CA10-4292-98F3-F0BD859E793D.png -------------------------------------------------------------------------------- /Shader/Pic/438BDB5F-8829-4687-9576-EA4A0AEEAC77.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/438BDB5F-8829-4687-9576-EA4A0AEEAC77.png -------------------------------------------------------------------------------- /Shader/Pic/4A8464B9-64D6-4A9B-8AE9-0BECBD93FA03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/4A8464B9-64D6-4A9B-8AE9-0BECBD93FA03.png -------------------------------------------------------------------------------- /Shader/Pic/4CC06D2C-1707-4F0E-8194-6802333DFFE8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/4CC06D2C-1707-4F0E-8194-6802333DFFE8.png -------------------------------------------------------------------------------- /Shader/Pic/526EF765-1589-4D0A-85E3-AE5998B6D235.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/526EF765-1589-4D0A-85E3-AE5998B6D235.png -------------------------------------------------------------------------------- /Shader/Pic/629AE65D-4F06-40F9-B044-679FC22BB68A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/629AE65D-4F06-40F9-B044-679FC22BB68A.png -------------------------------------------------------------------------------- /Shader/Pic/68E16F96-99EC-467F-B16C-6A62717B940A.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/68E16F96-99EC-467F-B16C-6A62717B940A.png -------------------------------------------------------------------------------- /Shader/Pic/78975E1C-2C3E-4714-B075-7211453B6038.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/78975E1C-2C3E-4714-B075-7211453B6038.png -------------------------------------------------------------------------------- /Shader/Pic/8271BB78-D1A9-4511-9ED7-662E199DFDBA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/8271BB78-D1A9-4511-9ED7-662E199DFDBA.png -------------------------------------------------------------------------------- /Shader/Pic/94C91BDE-6E5F-492E-9CF7-EBCDF30B466E.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/94C91BDE-6E5F-492E-9CF7-EBCDF30B466E.png -------------------------------------------------------------------------------- /Shader/Pic/973883FD-33FF-484D-80DA-E5AFB640C2C2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/973883FD-33FF-484D-80DA-E5AFB640C2C2.jpg -------------------------------------------------------------------------------- /Shader/Pic/A96C901D-A4DA-4306-91E0-254485A68C6F.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/A96C901D-A4DA-4306-91E0-254485A68C6F.png -------------------------------------------------------------------------------- /Shader/Pic/AF86A5F7-B324-45C5-A912-491FD5A62045.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/AF86A5F7-B324-45C5-A912-491FD5A62045.png -------------------------------------------------------------------------------- /Shader/Pic/BC8C1D7F-4E86-4F62-9C49-CC1F6DA33FD8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/BC8C1D7F-4E86-4F62-9C49-CC1F6DA33FD8.jpg -------------------------------------------------------------------------------- /Shader/Pic/C25AA265-D417-4208-AF7A-6030BAE3CDC9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/C25AA265-D417-4208-AF7A-6030BAE3CDC9.png -------------------------------------------------------------------------------- /Shader/Pic/CC226FC0-6D7C-4889-B397-EF80189DDB3B.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/CC226FC0-6D7C-4889-B397-EF80189DDB3B.png -------------------------------------------------------------------------------- /Shader/Pic/D170E78A-7AEE-4766-B85D-72FC954524AF.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/D170E78A-7AEE-4766-B85D-72FC954524AF.png -------------------------------------------------------------------------------- /Shader/Pic/EAE2CF2B-BE19-445B-85B4-26E478EBF9CE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/EAE2CF2B-BE19-445B-85B4-26E478EBF9CE.png -------------------------------------------------------------------------------- /Shader/Pic/FD1887D1-5F60-45FC-A2F7-BEB9C0E7E299.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/Shader/Pic/FD1887D1-5F60-45FC-A2F7-BEB9C0E7E299.png -------------------------------------------------------------------------------- /Shader/Shader_Blend.md: -------------------------------------------------------------------------------- 1 | # Shader 之 Blend 2 | 3 | ## 什么是 Blend? 4 | 5 | > `Blending` is used to make transparent objects. 6 | 7 | 8 | 9 | > When graphics are rendered, after all Shaders 10 | have executed and all Textures have been applied, the pixels 11 | are written to the screen. How they are combined with what is already there is controlled by the Blend command. 12 | 13 | 上面这段话是 Unity 官方对 `Blend` 命令的解释,意思大致是“ `Blend` 命令控制与已经存在在 `GBuffer` 缓存中的像素进行组合渲染(是应该渲染 `GBuffer` 中的像素?还是原游戏物体的像素?还是说两个像素做一些组合操作再渲染?)。” 14 | 15 | ## 语法 16 | 17 | * `Blend Off`:关闭混合(这是默认值)。 18 | * `Blend SrcFactor DstFactor`:配置并启用混合。生成的颜色乘以 `SrcFactor`。屏幕上已有的颜色乘以 `DstFactor`,两者相加。 19 | * `Blend SrcFactor DstFactor, SrcFactorA DstFactorA`:与上面相同,但使用不同的因子来混合 `alpha` 通道。 20 | * `BlendOp Op`:不要将混合颜色添加到一起,而是对它们执行不同的操作。 21 | * `BlendOp OpColor, OpAlpha`:与上面相同,但对颜色`(RGB)`和 `alpha(A)`通道使用不同的混合操作。 22 | 23 | ### Blend 属性 24 | 25 | 往 `SrcFactor`/`SrcFactorA` 和 `DstFactor`/`DstFactorA` 上填的值。 26 | 27 | * `One`:混合因子 1,表示完全的源颜色或目标颜色。 28 | * `Zero`:混合因子 0,舍弃掉源颜色或目标颜色。 29 | * `SrcColor`:源颜色值 30 | * `SrcAlpha`:源透明度 31 | * `DstColor`:目标颜色值 32 | * `DstAlpha`:目标透明度 33 | * `OneMinusSrcColor`:1 - `SrcColor` 34 | * `OneMinusSrcAlpha`:1 - `SrcAlpha` 35 | * `OneMinusDstColor`:1 - `DstColor` 36 | * `OneMinusDstAlpha`:1 - `DstAlpha` 37 | 38 | ### [BlendOp 属性](https://docs.unity3d.com/ScriptReference/Rendering.BlendOp.html) 39 | 40 | * `Add`:加法 41 | `FinalColor = SrcFactor * SrcColor + DstFactor * DstColor` 42 | * `Sub`:减法(源-目标) 43 | `FinalColor = SrcFactor * SrcColor - DstFactor * DstColor` 44 | * `RevSub`:减法(目标-源) 45 | `FinalColor = DstFactor * DstColor - SrcFactor * SrcColor` 46 | * `Min`:较小值(逐个通道比较) 47 | * `Max`:较大值(逐个通道比较) 48 | 49 | 下面是一些目前仅限 [`DX11.1`](https://baike.baidu.com/item/DirectX%2011) 支持的逻辑运算混合操作。 50 | 51 | ⚠️ 注意:`s == SrcFactor`,`d == DstFactor` 52 | 53 | * `LogicalClear`:Clear (0) 54 | * `LogicalSet`:Set (1) 55 | * `LogicalCopy`:Copy (s) 56 | * `LogicalCopyInverted`:Copy inverted (!s) 57 | * `LogicalNoop`:Noop (d) 58 | * `LogicalInvert`:Invert (!d) 59 | * `LogicalAnd`:And (s & d) 60 | * `LogicalNand`:Nand !(s & d) 61 | * `LogicalOr`:Or (s | d) 62 | * `LogicalNor`:Nor !(s | d) 63 | * `LogicalXor`:Xor (s ^ d) 64 | * `LogicalEquiv`:Equivalence !(s ^ d) 65 | * `LogicalAndReverse`:Reverse And (s & !d) 66 | * `LogicalAndInverted`:Inverted And (!s & d) 67 | * `LogicalOrReverse`:Reverse Or (s | !d) 68 | * `LogicalOrInverted`:Inverted Or (!s | d) 69 | 70 | ## 语法分析 71 | 72 | 当使用 `Blend` 时,最终颜色被计算为 `result = fragment_color * SrcFactor + pixel_color * DstFactor`,这个 `fragment_color` 即是经过处理的片段颜色,也就是 `fragment`返回的结果,`pixel_color` 是原本存在于缓冲区`(GBuffer)`的颜色,这个 `result` 颜色被重新写入缓冲区,等待被其他片段 `Blend` 或者变成最终颜色(如果没有被其他 `Blend`)。 73 | 74 | 上面这段话是对 `语法` 中的 75 | 76 | > `Blend SrcFactor DstFactor:配置并启用混合。生成的颜色乘以 SrcFactor。屏幕上已有的颜色乘以 DstFactor,两者相加。` 77 | 78 | 的分析,其中的 `生成的颜色` 是指 `fragment` 返回的结果,`屏幕上已有的颜色` 对应上面的 `pixel_color` 是指 `原本存在于缓冲区(GBuffer)`的颜色 。 79 | 80 | 除去 `Blend` 参数,上述公式中的加号也是可以配置的,这个就是`BlendOp`。 81 | 82 | ## 常见 Blend 组合及效果 83 | 84 | 85 | 86 | 上图为无任何 `Blend` 命令的情况下 ,左边为 `Scene` 视图,右边为 `Game` 视图。 87 | 88 | **前提条件:** 89 | 90 | 1. 立方体在前面,白色球体在立方体的后面。 91 | 2. 立方体的 `Tags` 为 `{ "RenderType"="Transprant" "Queue" = "Transparent" }`
92 | 白色球体的 `Tags` 为 `{ "RenderType"="Transprant" "Queue" = "Transparent+100" }`,`Queue`使用 `Transprant` 保证了 `立方体` 在 `Game` 视图中不为黑色,白色球体的 `Queue` 为 `Transparent+100` 保证了白色球体比立方体后渲染。 93 | 94 | **⚠️ 注意:下面是修改的立方体的 Shader :** 95 | 96 | * `Blend One One`:立方体材质的像素和 `GBuffer`(天空盒)中的像素混合渲染。 97 | * `Blend One Zero`:只渲染立方体材质的像素。 98 | * `Blend Zero One`:只渲染 `GBuffer`(天空盒)中的像素。 99 | * `Blend SrcAlpha OneMinusSrcAlpha`:传统透明度 100 | * `Blend SrcColor zero`:只显示立方体材质的源颜色 101 | * `Blend SrcColor One`:混合立方体材质的源颜色和 `GBuffer`(天空盒)的源颜色 102 | 103 | 更多的组合请自行去尝试。 104 | 105 | 下面将通过[风宇冲博客](http://blog.sina.com.cn/s/blog_471132920101d8z5.html)中的几个具体的数值计算的小例子来加深理解,以 `Blend SrcAlpha OneMinusSrcAlpha` 为 🌰 : 106 | 107 | 翻译成中文就是 `最终颜色 = 源颜色 * 源透明值 + 目标颜色 * (1 - 源透明值)`。 108 | 109 | 1. 假设贴图有一个不透明红色点 `Color(1, 0, 0, 1)`,该点背景色为不透明蓝色 `Color(0, 0, 1, 1)`。 110 | 111 | `最终颜色 = (1, 0, 0) * 1 + (0, 0, 1) * (1 - 1) = (1, 0, 0)` 112 | 113 | 结论一:贴图 alpha 值为 1 时,仅显示贴图,不显示背景。 114 | 115 | 2. 假设贴图有一个透明红色点 `Color(1, 0, 0, 0)`,该点背景色为透明,但 `B` 通道值为 1,即 `Color(0, 0, 1, 0)`。 116 | 117 | `最终颜色 = (1, 0, 0) * 0 + (0, 0, 1) * (1 - 0) = (0, 0, 1)` 118 | 119 | 结论二:贴图 alpha 值为 0 时,仅显示混合目标即背景,不显示贴图。 120 | 121 | 但是目标 alpha 值为 0,即其实这个点的背景是透明的,而我们却把它显示出来了,这就不对了。 122 | 123 | 经验:带 A 通道的贴图中,不只 A 值为 0,RGB 值也要为 0,不然容易出错。 124 | 125 | 3. 假设贴图有一个半透明红色点 126 | `Color(1, 0, 0, 0.8)`,该点背景色为不透明蓝色 127 |  `Color(0, 0, 1, 1)`。 128 | 129 | `最终颜色 = (1, 0, 0) * 0.8 + (0, 0, 1) * (1 - 0.8) = (0.8, 0, 0.2)` 130 | 131 | 而假如 0.8 变为 0.2 时, 132 | 133 | `最终颜色 = (1, 0, 0) * 0.2 + (0, 0, 1) * (1 - 0.2) = (0.2, 0, 0.8)` 134 | 135 | 结论:贴图 alpha 值越大,颜色越偏向贴图;alpha 值越小,颜色越偏向混合目标。 136 | 137 | **参考文章:** 138 | 139 | * [ShaderLab: Blending](https://docs.unity3d.com/Manual/SL-Blend.html) 140 | * [Shader入门(十八)混合(Blend)命令](https://gameinstitute.qq.com/community/detail/121996) 141 | * [Shader第十三讲 Alpha混合](http://blog.sina.com.cn/s/blog_471132920101d8z5.html) 142 | 143 | 144 | 如有内容有误,欢迎 [issue](https://github.com/CYBoys/Blogs/issues/new) 指正。 145 | 146 | **转载请注明出处!** 147 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 831E16992008D32700CBA688 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 831E16982008D32700CBA688 /* AppDelegate.m */; }; 11 | 831E169C2008D32700CBA688 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 831E169B2008D32700CBA688 /* ViewController.m */; }; 12 | 831E169F2008D32700CBA688 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 831E169D2008D32700CBA688 /* Main.storyboard */; }; 13 | 831E16A12008D32700CBA688 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 831E16A02008D32700CBA688 /* Assets.xcassets */; }; 14 | 831E16A42008D32700CBA688 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 831E16A22008D32700CBA688 /* LaunchScreen.storyboard */; }; 15 | 831E16A72008D32700CBA688 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 831E16A62008D32700CBA688 /* main.m */; }; 16 | 831E16E3200B92C500CBA688 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 831E169B2008D32700CBA688 /* ViewController.m */; }; 17 | 831E16E4200B92C500CBA688 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 831E16A62008D32700CBA688 /* main.m */; }; 18 | 831E16E5200B92C500CBA688 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 831E16982008D32700CBA688 /* AppDelegate.m */; }; 19 | 831E16E8200B92C500CBA688 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 831E16A22008D32700CBA688 /* LaunchScreen.storyboard */; }; 20 | 831E16E9200B92C500CBA688 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 831E16A02008D32700CBA688 /* Assets.xcassets */; }; 21 | 831E16EA200B92C500CBA688 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 831E169D2008D32700CBA688 /* Main.storyboard */; }; 22 | 831E16F1200C345D00CBA688 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 831E16A52008D32700CBA688 /* Info.plist */; }; 23 | 831E16F3200C3E6300CBA688 /* CDDemo2Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 831E16F2200C3E6300CBA688 /* CDDemo2Info.plist */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 831E16942008D32700CBA688 /* CDDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CDDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 831E16972008D32700CBA688 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 29 | 831E16982008D32700CBA688 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 30 | 831E169A2008D32700CBA688 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 31 | 831E169B2008D32700CBA688 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 32 | 831E169E2008D32700CBA688 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 33 | 831E16A02008D32700CBA688 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | 831E16A32008D32700CBA688 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 35 | 831E16A52008D32700CBA688 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 831E16A62008D32700CBA688 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 37 | 831E16EE200B92C500CBA688 /* CDDemo2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CDDemo2.app; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 831E16F2200C3E6300CBA688 /* CDDemo2Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CDDemo2Info.plist; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 831E16912008D32700CBA688 /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | 831E16E6200B92C500CBA688 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | 831E168B2008D32700CBA688 = { 60 | isa = PBXGroup; 61 | children = ( 62 | 831E16962008D32700CBA688 /* CDDemo */, 63 | 831E16952008D32700CBA688 /* Products */, 64 | ); 65 | sourceTree = ""; 66 | }; 67 | 831E16952008D32700CBA688 /* Products */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 831E16942008D32700CBA688 /* CDDemo.app */, 71 | 831E16EE200B92C500CBA688 /* CDDemo2.app */, 72 | ); 73 | name = Products; 74 | sourceTree = ""; 75 | }; 76 | 831E16962008D32700CBA688 /* CDDemo */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 831E16972008D32700CBA688 /* AppDelegate.h */, 80 | 831E16982008D32700CBA688 /* AppDelegate.m */, 81 | 831E169A2008D32700CBA688 /* ViewController.h */, 82 | 831E169B2008D32700CBA688 /* ViewController.m */, 83 | 831E169D2008D32700CBA688 /* Main.storyboard */, 84 | 831E16A02008D32700CBA688 /* Assets.xcassets */, 85 | 831E16A22008D32700CBA688 /* LaunchScreen.storyboard */, 86 | 831E16A52008D32700CBA688 /* Info.plist */, 87 | 831E16F2200C3E6300CBA688 /* CDDemo2Info.plist */, 88 | 831E16A62008D32700CBA688 /* main.m */, 89 | ); 90 | path = CDDemo; 91 | sourceTree = ""; 92 | }; 93 | /* End PBXGroup section */ 94 | 95 | /* Begin PBXNativeTarget section */ 96 | 831E16932008D32700CBA688 /* CDDemo */ = { 97 | isa = PBXNativeTarget; 98 | buildConfigurationList = 831E16AA2008D32700CBA688 /* Build configuration list for PBXNativeTarget "CDDemo" */; 99 | buildPhases = ( 100 | 831E16902008D32700CBA688 /* Sources */, 101 | 831E16912008D32700CBA688 /* Frameworks */, 102 | 831E16922008D32700CBA688 /* Resources */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = CDDemo; 109 | productName = CDDemo; 110 | productReference = 831E16942008D32700CBA688 /* CDDemo.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | 831E16E1200B92C500CBA688 /* CDDemo2 */ = { 114 | isa = PBXNativeTarget; 115 | buildConfigurationList = 831E16EB200B92C500CBA688 /* Build configuration list for PBXNativeTarget "CDDemo2" */; 116 | buildPhases = ( 117 | 831E16E2200B92C500CBA688 /* Sources */, 118 | 831E16E6200B92C500CBA688 /* Frameworks */, 119 | 831E16E7200B92C500CBA688 /* Resources */, 120 | ); 121 | buildRules = ( 122 | ); 123 | dependencies = ( 124 | ); 125 | name = CDDemo2; 126 | productName = CDDemo; 127 | productReference = 831E16EE200B92C500CBA688 /* CDDemo2.app */; 128 | productType = "com.apple.product-type.application"; 129 | }; 130 | /* End PBXNativeTarget section */ 131 | 132 | /* Begin PBXProject section */ 133 | 831E168C2008D32700CBA688 /* Project object */ = { 134 | isa = PBXProject; 135 | attributes = { 136 | LastUpgradeCheck = 0920; 137 | ORGANIZATIONNAME = LaiYoung_; 138 | TargetAttributes = { 139 | 831E16932008D32700CBA688 = { 140 | CreatedOnToolsVersion = 9.2; 141 | ProvisioningStyle = Manual; 142 | }; 143 | 831E16E1200B92C500CBA688 = { 144 | ProvisioningStyle = Manual; 145 | }; 146 | }; 147 | }; 148 | buildConfigurationList = 831E168F2008D32700CBA688 /* Build configuration list for PBXProject "CDDemo" */; 149 | compatibilityVersion = "Xcode 8.0"; 150 | developmentRegion = en; 151 | hasScannedForEncodings = 0; 152 | knownRegions = ( 153 | en, 154 | Base, 155 | ); 156 | mainGroup = 831E168B2008D32700CBA688; 157 | productRefGroup = 831E16952008D32700CBA688 /* Products */; 158 | projectDirPath = ""; 159 | projectRoot = ""; 160 | targets = ( 161 | 831E16932008D32700CBA688 /* CDDemo */, 162 | 831E16E1200B92C500CBA688 /* CDDemo2 */, 163 | ); 164 | }; 165 | /* End PBXProject section */ 166 | 167 | /* Begin PBXResourcesBuildPhase section */ 168 | 831E16922008D32700CBA688 /* Resources */ = { 169 | isa = PBXResourcesBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | 831E16F1200C345D00CBA688 /* Info.plist in Resources */, 173 | 831E16A42008D32700CBA688 /* LaunchScreen.storyboard in Resources */, 174 | 831E16A12008D32700CBA688 /* Assets.xcassets in Resources */, 175 | 831E16F3200C3E6300CBA688 /* CDDemo2Info.plist in Resources */, 176 | 831E169F2008D32700CBA688 /* Main.storyboard in Resources */, 177 | ); 178 | runOnlyForDeploymentPostprocessing = 0; 179 | }; 180 | 831E16E7200B92C500CBA688 /* Resources */ = { 181 | isa = PBXResourcesBuildPhase; 182 | buildActionMask = 2147483647; 183 | files = ( 184 | 831E16E8200B92C500CBA688 /* LaunchScreen.storyboard in Resources */, 185 | 831E16E9200B92C500CBA688 /* Assets.xcassets in Resources */, 186 | 831E16EA200B92C500CBA688 /* Main.storyboard in Resources */, 187 | ); 188 | runOnlyForDeploymentPostprocessing = 0; 189 | }; 190 | /* End PBXResourcesBuildPhase section */ 191 | 192 | /* Begin PBXSourcesBuildPhase section */ 193 | 831E16902008D32700CBA688 /* Sources */ = { 194 | isa = PBXSourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | 831E169C2008D32700CBA688 /* ViewController.m in Sources */, 198 | 831E16A72008D32700CBA688 /* main.m in Sources */, 199 | 831E16992008D32700CBA688 /* AppDelegate.m in Sources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | 831E16E2200B92C500CBA688 /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 831E16E3200B92C500CBA688 /* ViewController.m in Sources */, 208 | 831E16E4200B92C500CBA688 /* main.m in Sources */, 209 | 831E16E5200B92C500CBA688 /* AppDelegate.m in Sources */, 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | }; 213 | /* End PBXSourcesBuildPhase section */ 214 | 215 | /* Begin PBXVariantGroup section */ 216 | 831E169D2008D32700CBA688 /* Main.storyboard */ = { 217 | isa = PBXVariantGroup; 218 | children = ( 219 | 831E169E2008D32700CBA688 /* Base */, 220 | ); 221 | name = Main.storyboard; 222 | sourceTree = ""; 223 | }; 224 | 831E16A22008D32700CBA688 /* LaunchScreen.storyboard */ = { 225 | isa = PBXVariantGroup; 226 | children = ( 227 | 831E16A32008D32700CBA688 /* Base */, 228 | ); 229 | name = LaunchScreen.storyboard; 230 | sourceTree = ""; 231 | }; 232 | /* End PBXVariantGroup section */ 233 | 234 | /* Begin XCBuildConfiguration section */ 235 | 831E16A82008D32700CBA688 /* Debug */ = { 236 | isa = XCBuildConfiguration; 237 | buildSettings = { 238 | ALWAYS_SEARCH_USER_PATHS = NO; 239 | CLANG_ANALYZER_NONNULL = YES; 240 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 241 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 242 | CLANG_CXX_LIBRARY = "libc++"; 243 | CLANG_ENABLE_MODULES = YES; 244 | CLANG_ENABLE_OBJC_ARC = YES; 245 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 246 | CLANG_WARN_BOOL_CONVERSION = YES; 247 | CLANG_WARN_COMMA = YES; 248 | CLANG_WARN_CONSTANT_CONVERSION = YES; 249 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 250 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 251 | CLANG_WARN_EMPTY_BODY = YES; 252 | CLANG_WARN_ENUM_CONVERSION = YES; 253 | CLANG_WARN_INFINITE_RECURSION = YES; 254 | CLANG_WARN_INT_CONVERSION = YES; 255 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 259 | CLANG_WARN_STRICT_PROTOTYPES = YES; 260 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 261 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 262 | CLANG_WARN_UNREACHABLE_CODE = YES; 263 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 264 | CODE_SIGN_IDENTITY = "iPhone Developer"; 265 | COPY_PHASE_STRIP = NO; 266 | DEBUG_INFORMATION_FORMAT = dwarf; 267 | ENABLE_STRICT_OBJC_MSGSEND = YES; 268 | ENABLE_TESTABILITY = YES; 269 | GCC_C_LANGUAGE_STANDARD = gnu11; 270 | GCC_DYNAMIC_NO_PIC = NO; 271 | GCC_NO_COMMON_BLOCKS = YES; 272 | GCC_OPTIMIZATION_LEVEL = 0; 273 | GCC_PREPROCESSOR_DEFINITIONS = ( 274 | "DEBUG=1", 275 | "$(inherited)", 276 | ); 277 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 278 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 279 | GCC_WARN_UNDECLARED_SELECTOR = YES; 280 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 281 | GCC_WARN_UNUSED_FUNCTION = YES; 282 | GCC_WARN_UNUSED_VARIABLE = YES; 283 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 284 | MTL_ENABLE_DEBUG_INFO = YES; 285 | ONLY_ACTIVE_ARCH = YES; 286 | SDKROOT = iphoneos; 287 | }; 288 | name = Debug; 289 | }; 290 | 831E16A92008D32700CBA688 /* Release */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ALWAYS_SEARCH_USER_PATHS = NO; 294 | CLANG_ANALYZER_NONNULL = YES; 295 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 297 | CLANG_CXX_LIBRARY = "libc++"; 298 | CLANG_ENABLE_MODULES = YES; 299 | CLANG_ENABLE_OBJC_ARC = YES; 300 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 301 | CLANG_WARN_BOOL_CONVERSION = YES; 302 | CLANG_WARN_COMMA = YES; 303 | CLANG_WARN_CONSTANT_CONVERSION = YES; 304 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 305 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 306 | CLANG_WARN_EMPTY_BODY = YES; 307 | CLANG_WARN_ENUM_CONVERSION = YES; 308 | CLANG_WARN_INFINITE_RECURSION = YES; 309 | CLANG_WARN_INT_CONVERSION = YES; 310 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 311 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 312 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 313 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 314 | CLANG_WARN_STRICT_PROTOTYPES = YES; 315 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 316 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 317 | CLANG_WARN_UNREACHABLE_CODE = YES; 318 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 319 | CODE_SIGN_IDENTITY = "iPhone Developer"; 320 | COPY_PHASE_STRIP = NO; 321 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 322 | ENABLE_NS_ASSERTIONS = NO; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | GCC_C_LANGUAGE_STANDARD = gnu11; 325 | GCC_NO_COMMON_BLOCKS = YES; 326 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 327 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 328 | GCC_WARN_UNDECLARED_SELECTOR = YES; 329 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 330 | GCC_WARN_UNUSED_FUNCTION = YES; 331 | GCC_WARN_UNUSED_VARIABLE = YES; 332 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 333 | MTL_ENABLE_DEBUG_INFO = NO; 334 | SDKROOT = iphoneos; 335 | VALIDATE_PRODUCT = YES; 336 | }; 337 | name = Release; 338 | }; 339 | 831E16AB2008D32700CBA688 /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 343 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 344 | CODE_SIGN_STYLE = Manual; 345 | DEVELOPMENT_TEAM = 7BT4LD3H35; 346 | INFOPLIST_FILE = CDDemo/Info.plist; 347 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 348 | PRODUCT_BUNDLE_IDENTIFIER = com.laiyoung.CDTestDemo; 349 | PRODUCT_NAME = "$(TARGET_NAME)"; 350 | PROVISIONING_PROFILE = "347eb17e-b31d-4445-810c-cbc9ae5ae9f9"; 351 | PROVISIONING_PROFILE_SPECIFIER = QTCDTestDemo; 352 | TARGETED_DEVICE_FAMILY = 1; 353 | }; 354 | name = Debug; 355 | }; 356 | 831E16AC2008D32700CBA688 /* Release */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 360 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 361 | CODE_SIGN_STYLE = Manual; 362 | DEVELOPMENT_TEAM = 7BT4LD3H35; 363 | INFOPLIST_FILE = CDDemo/Info.plist; 364 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 365 | PRODUCT_BUNDLE_IDENTIFIER = com.laiyoung.CDTestDemo; 366 | PRODUCT_NAME = "$(TARGET_NAME)"; 367 | PROVISIONING_PROFILE = "347eb17e-b31d-4445-810c-cbc9ae5ae9f9"; 368 | PROVISIONING_PROFILE_SPECIFIER = QTCDTestDemo; 369 | TARGETED_DEVICE_FAMILY = 1; 370 | }; 371 | name = Release; 372 | }; 373 | 831E16EC200B92C500CBA688 /* Debug */ = { 374 | isa = XCBuildConfiguration; 375 | buildSettings = { 376 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 377 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 378 | CODE_SIGN_STYLE = Manual; 379 | DEVELOPMENT_TEAM = 7BT4LD3H35; 380 | INFOPLIST_FILE = "$(SRCROOT)/CDDemo/CDDemo2Info.plist"; 381 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 382 | PRODUCT_BUNDLE_IDENTIFIER = com.laiyoung.CDTestDemo2; 383 | PRODUCT_NAME = "$(TARGET_NAME)"; 384 | PROVISIONING_PROFILE = "399fdbd7-f156-49e3-b412-7e12cbd08c26"; 385 | PROVISIONING_PROFILE_SPECIFIER = QTCDTestDemo2; 386 | TARGETED_DEVICE_FAMILY = 1; 387 | }; 388 | name = Debug; 389 | }; 390 | 831E16ED200B92C500CBA688 /* Release */ = { 391 | isa = XCBuildConfiguration; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; 395 | CODE_SIGN_STYLE = Manual; 396 | DEVELOPMENT_TEAM = 7BT4LD3H35; 397 | INFOPLIST_FILE = "$(SRCROOT)/CDDemo/CDDemo2Info.plist"; 398 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 399 | PRODUCT_BUNDLE_IDENTIFIER = com.laiyoung.CDTestDemo2; 400 | PRODUCT_NAME = "$(TARGET_NAME)"; 401 | PROVISIONING_PROFILE = "399fdbd7-f156-49e3-b412-7e12cbd08c26"; 402 | PROVISIONING_PROFILE_SPECIFIER = QTCDTestDemo2; 403 | TARGETED_DEVICE_FAMILY = 1; 404 | }; 405 | name = Release; 406 | }; 407 | /* End XCBuildConfiguration section */ 408 | 409 | /* Begin XCConfigurationList section */ 410 | 831E168F2008D32700CBA688 /* Build configuration list for PBXProject "CDDemo" */ = { 411 | isa = XCConfigurationList; 412 | buildConfigurations = ( 413 | 831E16A82008D32700CBA688 /* Debug */, 414 | 831E16A92008D32700CBA688 /* Release */, 415 | ); 416 | defaultConfigurationIsVisible = 0; 417 | defaultConfigurationName = Release; 418 | }; 419 | 831E16AA2008D32700CBA688 /* Build configuration list for PBXNativeTarget "CDDemo" */ = { 420 | isa = XCConfigurationList; 421 | buildConfigurations = ( 422 | 831E16AB2008D32700CBA688 /* Debug */, 423 | 831E16AC2008D32700CBA688 /* Release */, 424 | ); 425 | defaultConfigurationIsVisible = 0; 426 | defaultConfigurationName = Release; 427 | }; 428 | 831E16EB200B92C500CBA688 /* Build configuration list for PBXNativeTarget "CDDemo2" */ = { 429 | isa = XCConfigurationList; 430 | buildConfigurations = ( 431 | 831E16EC200B92C500CBA688 /* Debug */, 432 | 831E16ED200B92C500CBA688 /* Release */, 433 | ); 434 | defaultConfigurationIsVisible = 0; 435 | defaultConfigurationName = Release; 436 | }; 437 | /* End XCConfigurationList section */ 438 | }; 439 | rootObject = 831E168C2008D32700CBA688 /* Project object */; 440 | } 441 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo.xcodeproj/xcshareddata/xcschemes/CDDemo copy.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo.xcodeproj/xcshareddata/xcschemes/CDDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CDDemo 4 | // 5 | // Created by LaiYoung_ on 2018/1/12. 6 | // Copyright © 2018年 LaiYoung_. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CDDemo 4 | // 5 | // Created by LaiYoung_ on 2018/1/12. 6 | // Copyright © 2018年 LaiYoung_. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Small.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-Small@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-Small@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "Icon.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "Icon@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "1024x1024", 71 | "idiom" : "ios-marketing", 72 | "filename" : "Icon-1024.png", 73 | "scale" : "1x" 74 | } 75 | ], 76 | "info" : { 77 | "version" : 1, 78 | "author" : "xcode" 79 | } 80 | } -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-1024.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/CDDemo/Assets.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/CDDemo2Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 3.0 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 2.0 19 | CFBundleVersion 20 | 10 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CDDemo 4 | // 5 | // Created by LaiYoung_ on 2018/1/12. 6 | // Copyright © 2018年 LaiYoung_. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CDDemo 4 | // 5 | // Created by LaiYoung_ on 2018/1/12. 6 | // Copyright © 2018年 LaiYoung_. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | 23 | - (void)didReceiveMemoryWarning { 24 | [super didReceiveMemoryWarning]; 25 | // Dispose of any resources that can be recreated. 26 | } 27 | 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/CDDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CDDemo 4 | // 5 | // Created by LaiYoung_ on 2018/1/12. 6 | // Copyright © 2018年 LaiYoung_. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "fastlane" -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (2.3.6) 5 | addressable (2.5.2) 6 | public_suffix (>= 2.0.2, < 4.0) 7 | babosa (1.0.2) 8 | claide (1.0.2) 9 | colored (1.2) 10 | colored2 (3.1.2) 11 | commander-fastlane (4.4.5) 12 | highline (~> 1.7.2) 13 | declarative (0.0.10) 14 | declarative-option (0.1.0) 15 | domain_name (0.5.20170404) 16 | unf (>= 0.0.5, < 1.0.0) 17 | dotenv (2.2.1) 18 | excon (0.60.0) 19 | faraday (0.14.0) 20 | multipart-post (>= 1.2, < 3) 21 | faraday-cookie_jar (0.0.6) 22 | faraday (>= 0.7.4) 23 | http-cookie (~> 1.0.0) 24 | faraday_middleware (0.12.2) 25 | faraday (>= 0.7.4, < 1.0) 26 | fastimage (2.1.1) 27 | fastlane (2.77.1) 28 | CFPropertyList (>= 2.3, < 3.0.0) 29 | addressable (>= 2.3, < 3.0.0) 30 | babosa (>= 1.0.2, < 2.0.0) 31 | bundler (>= 1.12.0, < 2.0.0) 32 | colored 33 | commander-fastlane (>= 4.4.5, < 5.0.0) 34 | dotenv (>= 2.1.1, < 3.0.0) 35 | excon (>= 0.45.0, < 1.0.0) 36 | faraday (~> 0.9) 37 | faraday-cookie_jar (~> 0.0.6) 38 | faraday_middleware (~> 0.9) 39 | fastimage (>= 2.1.0, < 3.0.0) 40 | gh_inspector (>= 1.0.1, < 2.0.0) 41 | google-api-client (>= 0.13.1, < 0.14.0) 42 | highline (>= 1.7.2, < 2.0.0) 43 | json (< 3.0.0) 44 | mini_magick (~> 4.5.1) 45 | multi_json 46 | multi_xml (~> 0.5) 47 | multipart-post (~> 2.0.0) 48 | plist (>= 3.1.0, < 4.0.0) 49 | public_suffix (~> 2.0.0) 50 | rubyzip (>= 1.1.0, < 2.0.0) 51 | security (= 0.1.3) 52 | slack-notifier (>= 2.0.0, < 3.0.0) 53 | terminal-notifier (>= 1.6.2, < 2.0.0) 54 | terminal-table (>= 1.4.5, < 2.0.0) 55 | tty-screen (>= 0.6.3, < 1.0.0) 56 | tty-spinner (>= 0.8.0, < 1.0.0) 57 | word_wrap (~> 1.0.0) 58 | xcodeproj (>= 1.5.2, < 2.0.0) 59 | xcpretty (>= 0.2.4, < 1.0.0) 60 | xcpretty-travis-formatter (>= 0.0.3) 61 | gh_inspector (1.0.3) 62 | google-api-client (0.13.6) 63 | addressable (~> 2.5, >= 2.5.1) 64 | googleauth (~> 0.5) 65 | httpclient (>= 2.8.1, < 3.0) 66 | mime-types (~> 3.0) 67 | representable (~> 3.0) 68 | retriable (>= 2.0, < 4.0) 69 | googleauth (0.6.2) 70 | faraday (~> 0.12) 71 | jwt (>= 1.4, < 3.0) 72 | logging (~> 2.0) 73 | memoist (~> 0.12) 74 | multi_json (~> 1.11) 75 | os (~> 0.9) 76 | signet (~> 0.7) 77 | highline (1.7.10) 78 | http-cookie (1.0.3) 79 | domain_name (~> 0.5) 80 | httpclient (2.8.3) 81 | json (2.1.0) 82 | jwt (2.1.0) 83 | little-plugger (1.1.4) 84 | logging (2.2.2) 85 | little-plugger (~> 1.1) 86 | multi_json (~> 1.10) 87 | memoist (0.16.0) 88 | mime-types (3.1) 89 | mime-types-data (~> 3.2015) 90 | mime-types-data (3.2016.0521) 91 | mini_magick (4.5.1) 92 | multi_json (1.13.1) 93 | multi_xml (0.6.0) 94 | multipart-post (2.0.0) 95 | nanaimo (0.2.3) 96 | os (0.9.6) 97 | plist (3.4.0) 98 | public_suffix (2.0.5) 99 | representable (3.0.4) 100 | declarative (< 0.1.0) 101 | declarative-option (< 0.2.0) 102 | uber (< 0.2.0) 103 | retriable (3.1.1) 104 | rouge (2.0.7) 105 | rubyzip (1.2.1) 106 | security (0.1.3) 107 | signet (0.8.1) 108 | addressable (~> 2.3) 109 | faraday (~> 0.9) 110 | jwt (>= 1.5, < 3.0) 111 | multi_json (~> 1.10) 112 | slack-notifier (2.3.2) 113 | terminal-notifier (1.8.0) 114 | terminal-table (1.8.0) 115 | unicode-display_width (~> 1.1, >= 1.1.1) 116 | tty-cursor (0.5.0) 117 | tty-screen (0.6.4) 118 | tty-spinner (0.8.0) 119 | tty-cursor (>= 0.5.0) 120 | uber (0.1.0) 121 | unf (0.1.4) 122 | unf_ext 123 | unf_ext (0.0.7.4) 124 | unicode-display_width (1.3.0) 125 | word_wrap (1.0.0) 126 | xcodeproj (1.5.4) 127 | CFPropertyList (~> 2.3.3) 128 | claide (>= 1.0.2, < 2.0) 129 | colored2 (~> 3.1) 130 | nanaimo (~> 0.2.3) 131 | xcpretty (0.2.8) 132 | rouge (~> 2.0.7) 133 | xcpretty-travis-formatter (1.0.0) 134 | xcpretty (~> 0.2, >= 0.0.7) 135 | 136 | PLATFORMS 137 | ruby 138 | 139 | DEPENDENCIES 140 | fastlane 141 | 142 | BUNDLED WITH 143 | 1.16.1 144 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/.env.TargetA: -------------------------------------------------------------------------------- 1 | #APP唯一标识符 2 | APP_IDENTIFIER = "com.targetA.CDTestDemo" 3 | 4 | # 发布版本号 5 | APP_VERSION_RELEASE = "3.4.5" 6 | 7 | #自动提交审核 8 | SUBMIT_FOR_REVIEW = false 9 | 10 | #苹果开发者账号 11 | APPLE_ID = "appleid@icloud.com" 12 | 13 | # 团队ID 14 | TEAM_ID = "team_id" 15 | 16 | # 团队name 17 | TEAM_NAME = "TEAM_NAME" 18 | 19 | # Copytight 20 | COPYRIGHT = 'COPYRIGHT' 21 | 22 | # 新版本修改记录 23 | RELEASE_NOTES = "iOS 持续交付测试demo" 24 | 25 | # 支持网址 26 | SUPPORT_URL = 'https://www.xxxx.com' 27 | 28 | #.... 等等 -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/.env.TargetB: -------------------------------------------------------------------------------- 1 | #APP唯一标识符 2 | APP_IDENTIFIER = "com.targetB.CDTestDemo" 3 | 4 | # 发布版本号 5 | APP_VERSION_RELEASE = "2.3.4" 6 | 7 | #自动提交审核 8 | SUBMIT_FOR_REVIEW = false 9 | 10 | #苹果开发者账号 11 | APPLE_ID = "appleid@icloud.com" 12 | 13 | # 团队ID 14 | TEAM_ID = "team_id" 15 | 16 | # 团队name 17 | TEAM_NAME = "TEAM_NAME" 18 | 19 | # Copytight 20 | COPYRIGHT = 'COPYRIGHT' 21 | 22 | # 新版本修改记录 23 | RELEASE_NOTES = "iOS 持续交付测试demoB" 24 | 25 | # 支持网址 26 | SUPPORT_URL = 'https://www.xxxx.com' 27 | 28 | SCHEME = 'CDDemo copy' 29 | 30 | #.... 等等 -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/Appfile: -------------------------------------------------------------------------------- 1 | #The bundle identifier of your app 2 | app_identifier ENV['APP_IDENTIFIER'] 3 | 4 | # Your Apple email address 5 | apple_id ENV['APPLE_ID'] 6 | 7 | # Developer Portal Team ID 8 | team_id ENV['TEAM_ID'] -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/Deliverfile: -------------------------------------------------------------------------------- 1 | # app_identifier 2 | app_identifier ENV['APP_IDENTIFIER'] 3 | 4 | # 用户名,Apple ID电子邮件地址 5 | username ENV['APPLE_ID'] 6 | 7 | # 团队ID 8 | team_id ENV['TEAM_ID'] 9 | 10 | # 团队name 11 | team_name ENV['TEAM_NAME'] 12 | 13 | # copyright 14 | copyright ENV['COPYRIGHT'] 15 | 16 | # 关键字 17 | # keywords( 18 | # 'zh-Hans' => ENV['KEYWORDS'], 19 | # ) 20 | 21 | # 新版本修改记录 22 | # release_notes( 23 | # # 中国 24 | # 'zh-Hans' => ENV['RELEASE_NOTES'], 25 | # # 澳大利亚 26 | # #'en-au' => ENV['RELEASE_NOTES_AU'], 27 | # # 美国 28 | # #'en-us' => ENV['RELEASE_NOTES_US'] 29 | # ) 30 | 31 | # 支持网址 32 | # support_url( 33 | # # 中国 34 | # 'zh-Hans' => ENV['SUPPORT_URL'], 35 | # # 澳大利亚 36 | # #'en-au' => ENV['SUPPORT_URL_AU'], 37 | # # 美国 38 | # #'en-us' => ENV['SUPPORT_URL_US'] 39 | # ) 40 | 41 | # 隐私政策网址 国家代码 https://www.cnblogs.com/Mien/archive/2008/08/22/1273950.html 42 | # privacy_url( 43 | # # 中国 44 | # 'zh-Hans' => ENV['PRIVACY_URL'], 45 | # # 澳大利亚 46 | # #'en-au' => ENV['PRIVACY_URL_AU'], 47 | # # 美国 48 | # #'en-us' => ENV['PRIVACY_URL_US'] 49 | # ) 50 | 51 | # 上传完成后提交新版本进行审查 52 | submit_for_review false 53 | 54 | # 跳过HTML报告文件验证 55 | force true 56 | 57 | # 启用iTC的分阶段发布功能 灰度发布 58 | phased_release true 59 | 60 | # 应用审核小组的联系信息 app 审核信息 61 | app_review_information( 62 | first_name: "xx", 63 | last_name: "xx", 64 | phone_number: "+86 18888888888", 65 | email_address: "xxxx", 66 | demo_user: "test1@test.com", 67 | demo_password: "test123" 68 | ) -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # This file contains the fastlane.tools configuration 2 | # You can find the documentation at https://docs.fastlane.tools 3 | # 4 | # For a list of all available actions, check out 5 | # 6 | # https://docs.fastlane.tools/actions 7 | # 8 | 9 | # Uncomment the line if you want fastlane to automatically update itself 10 | # update_fastlane 11 | 12 | # 解决 xcodebuild -showBuildSettings timed-out after 10 seconds and 3 retries. You can override the timeout value with the environment variable FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT, 13 | # and the number of retries with the environment variable FASTLANE_XCODEBUILD_SETTINGS_RETRIES 14 | ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "180" 15 | ENV["FASTLANE_XCODE_LIST_TIMEOUT"] = "180" 16 | 17 | 18 | default_platform(:ios) 19 | 20 | platform :ios do 21 | 22 | # private_lane 表示私有lane,使用'bundle exec fastlane'命令,Archive_TargetA lane是不是显示出来的。 23 | private_lane :Archive_TargetA do |options| 24 | scheme = options[:scheme] 25 | date = Time.new.strftime("%Y%m%d-%h%M") 26 | 27 | # export_method 支持 app-store, ad-hoc, package, enterprise, development 28 | gym( 29 | scheme: "#{scheme}", 30 | output_name: "#{scheme}-#{date}.ipa", 31 | clean: true, 32 | export_method: 'app-store', 33 | ) 34 | 35 | # upload_to_app_store 36 | deliver # 同 upload_to_app_store 作用一样 37 | end 38 | 39 | private_lane :Archive_TargetB do |options| 40 | scheme = options[:scheme] 41 | date = Time.new.strftime("%Y%m%d-%h%M") 42 | 43 | # export_method 支持 app-store, ad-hoc, package, enterprise, development 44 | gym( 45 | scheme: "#{scheme}", 46 | output_name: "#{scheme}-#{date}.ipa", 47 | clean: true, 48 | export_method: 'app-store', 49 | ) 50 | 51 | # upload_to_app_store 52 | deliver # 同 upload_to_app_store 作用一样 53 | end 54 | 55 | 56 | lane :releaseTest do |options| 57 | app_identidier = ENV['APP_IDENTIFIER'] # 这时候读取出来的'APP_IDENTIFIER'就是'com.targetA.CDTestDemo',从'.env.TargetA'读取出来的 58 | 59 | puts "这是app_identidier #{app_identidier}" 60 | 61 | scheme = options[:scheme] 62 | 63 | puts "scheme 是 #{scheme}" 64 | 65 | parameter = options[:parameter] 66 | 67 | puts "参数是 #{parameter}" 68 | 69 | # # export_method 支持 app-store, ad-hoc, package, enterprise, development 70 | # gym( 71 | # scheme: "#{scheme}", 72 | # output_name: "#{scheme}.ipa", 73 | # clean: true, 74 | # export_method: 'app-store', 75 | # ) 76 | 77 | # deliver # 这时候deliverfile里面读取的内容就是从'.env.TargetA'文件读取的 78 | end 79 | 80 | #------------------------------传参------------------------------------------# 81 | 82 | desc "通过options传参数" 83 | lane :releaseDemo do |options| 84 | releaseTest(scheme: 'CDDemo') 85 | end 86 | 87 | desc "使用环境变量文件" 88 | lane :releaseDemo2 do 89 | sh "fastlane releaseTest --env TargetA" 90 | end 91 | 92 | 93 | desc "环境变量" 94 | lane :releaseDemo3 do 95 | sh "fastlane releaseTest parameter:\'12321哈哈\' --env TargetA" 96 | end 97 | 98 | 99 | #------------------------------打包统一入口------------------------------------------# 100 | 101 | desc "打包统一入口" 102 | lane :Archive do |options| 103 | # 如果我们传入的参数'type'是targetA,那么我们就执行Archive_TargetA 这个lane。。。 104 | type = options[:type] 105 | if type == "TargetA" 106 | Archive_TargetA(options) 107 | elsif type == "TargetB" 108 | Archive_TargetB(options) 109 | else 110 | Archive_TargetA(options) 111 | end 112 | end 113 | end 114 | 115 | 116 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | Install _fastlane_ using 12 | ``` 13 | [sudo] gem install fastlane -NV 14 | ``` 15 | or alternatively using `brew cask install fastlane` 16 | 17 | # Available Actions 18 | ## iOS 19 | ### ios releaseTest 20 | ``` 21 | fastlane ios releaseTest 22 | ``` 23 | 24 | ### ios releaseDemo 25 | ``` 26 | fastlane ios releaseDemo 27 | ``` 28 | 通过options传参数 29 | ### ios releaseDemo2 30 | ``` 31 | fastlane ios releaseDemo2 32 | ``` 33 | 使用环境变量文件 34 | ### ios releaseDemo3 35 | ``` 36 | fastlane ios releaseDemo3 37 | ``` 38 | 环境变量 39 | ### ios Archive 40 | ``` 41 | fastlane ios Archive 42 | ``` 43 | 打包统一入口 44 | 45 | ---- 46 | 47 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 48 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 49 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 50 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/copyright.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/primary_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/primary_first_sub_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/primary_second_sub_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/review_information/demo_password.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/review_information/demo_user.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/review_information/email_address.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/review_information/first_name.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/review_information/last_name.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/review_information/notes.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/review_information/phone_number.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/secondary_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/secondary_first_sub_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/secondary_second_sub_category.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/address_line1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/address_line1.txt -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/city_name.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/city_name.txt -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/country.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/country.txt -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/is_displayed_on_app_store.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/is_displayed_on_app_store.txt -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/postal_code.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/postal_code.txt -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/state.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/state.txt -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/trade_name.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/fastlane/metadata/trade_representative_contact_information/trade_name.txt -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/description.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/keywords.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/marketing_url.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/name.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LLLaiYoung/Blogs/982e406e165233a10cb80485278ad3e718a77454/iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/name.txt -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/privacy_url.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/promotional_text.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/release_notes.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/subtitle.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/metadata/zh-Hans/support_url.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /iOS/Examples/CDDemo/fastlane/screenshots/README.txt: -------------------------------------------------------------------------------- 1 | Put all screenshots you want to use inside the folder of its language (e.g. en-US). 2 | The device type will automatically be recognized using the image resolution. Apple TV screenshots 3 | should be stored in a subdirectory named appleTV with language folders inside of it. iMessage 4 | screenshots, like Apple TV screenshots, should also be stored in a subdirectory named iMessage 5 | with language folders inside of it. 6 | 7 | The screenshots can be named whatever you want, but keep in mind they are sorted alphabetically. 8 | -------------------------------------------------------------------------------- /iOS/ios-fastlane.md: -------------------------------------------------------------------------------- 1 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed39277c58076.png) 2 | 3 | 小目标:使用`Jenkins`一键构建,并自动上传到`App Store`。 4 | 5 | # 一、为什么选择 [Fastlane](https://docs.fastlane.tools/)? 6 | 7 | ![why fastlane](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed3a2a977adc4.png) 8 | 9 | `fastlane`是为`iOS`和`Android`应用程序自动化测试部署和发布的最简单方法。🚀它处理所有繁琐的任务,如生成屏幕截图,处理代码签名以及发布应用程序。 10 | 11 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed39dac1bfea0.png) 12 | 13 | ### 使用场景 14 | 15 | * 提交时执行测试(包括单元测试和集成测试)。 16 | * 构建并分发内部测试,公开测试版本。 17 | * 构建生产版本并上传至 ITC(包括更新配置文件,创建新的屏幕截图,上传应用并提交审核)。 18 | * … 19 | 20 | ### 工具集 21 | `fastlane` 将如下的工具套件有机地结合起来,从管理证书到单元测试,从编译打包到上传发布,都能通过命令行轻松完成.该套件支持与 `Jenkins` 和 `CocoaPods`,`xctools` 等其他第三方工具的集成,并且能够定义多个通道(lanes)以支持不同的部署目标。 22 | 23 | * 测试工具 24 | * scan:自动运行测试工具,可以生成漂亮的HTML报告。 25 | * 生成证书、配置工具 26 | * cert:自动创建iOS代码签名证书(.cert文件)。 27 | * sigh: 创建、更新、下载和修复 provisioning profiles,支持App Store, Ad Hoc, Development和企业profiles。 28 | * pem:自动生成、更新推送配置文件。 29 | * 截图、上传、描设备边框 30 | * deliver: 上传截图、元数据、App到iTunesConnect。 31 | * snapshot: 依靠 UI Test 完成截图。 32 | * frameit: 快速地把应用截图放入设备框里。 33 | * 自动化编译工具 34 | * gym: 编译、打包iOS app,生成签名的ipa文件 。 35 | * App 公测工具 36 | * pilot:管理TestFlight测试用户,上传二进制文件。 37 | * firim:管理firim。 38 | * … 39 | 40 | # 二、准备工作,很重要的哟 41 | 42 | 1. 配置当前设备环境,最新的`fastlane(2.75.1)`需要`2.1`以上的`ruby`版本,正常的版本低点的,也是要求`2.0`以上的。因为`fastlane`工具是使用`ruby`写的。版本过低的建议安装[`rvm`](https://juejin.im/post/5a35ee095188252b145b1ffc)来升级`ruby`。 43 | 44 | ``` 45 | curl -L get.rvm.io | bash -s stable 46 | 47 | source ~/.bashrc 48 | source ~/.bash_profile 49 | 50 | # 检测是否安装成功 51 | rvm -v 52 | ``` 53 | 54 | 2. 设置环境变量,`fastlane`需要设置一些环境变量才能正确运行,如果当前的语言环境没有设置为`UTF-8`,会导致构建和上传的时候出现问题。在`~/.bashrc`, `~/.bash_profile` 或者 `~/.zshrc` 文件下添加如下内容: 55 | 56 | ``` 57 | export LC_ALL=en_US.UTF-8 58 | export LANG=en_US.UTF-8 59 | ``` 60 | 61 | 3. 安装`Xcode`命令行工具 `xcode-select --install`,如果已经安装会提示`xcode-select: error: command line tools are already installed, use "Software Update" to install updates`。 62 | 4. 创建`App ID`,`证书`,在`iTunes connect` 创建一个用于测试的 app。 63 | 5. [安装fastlane](https://docs.fastlane.tools/getting-started/ios/setup/#choose-your-installation-method)。 64 | 6. 创建一个测试demo。 65 | 1. 并将其`scheme`设置为`shared`,不然`fastlane init`的时候会失败。 66 | 67 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ee2a30718e52a.png) 68 | 69 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ee2a9d754e46f.png) 70 | 71 | 2. 设置好签名配置文件。 72 | 3. 为app添加`icon`。 73 | 4. 修改`devices`为`iPad`或者`iPhone`。 74 | 75 | 76 | # 三、实践 77 | 78 | ## fastlane init 79 | 80 | 1. cd 到项目目录下,对于ruby安装程序,使用命令 `sudo fastlane init`。(swift使用`fastlane init swift`,**Swift安装仍在测试阶段。有关更多信息,请参阅[Fastlane.swift](https://docs.fastlane.tools/getting-started/ios/fastlane-swift/)文档。** ) 81 | 82 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ee2f3888ec01e.png) 83 | 84 | 2. 会问你想使用`fastlane`做什么?这里我们输入`3`,自动发布到`Apple Store`。 85 | 86 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed3d277057ae1.png) 87 | 88 | 执行过程中会要求你输入Apple开发证书的`Apple ID`,如果有多个Team,会让你选择team。 89 | 90 | 3. 接着会问是否想用`fastlane`来管理你的`app metadata`。 91 | 92 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed6f6d366332a.png) 93 | 94 | * 输入`y`,`fastlane`则会下载现有的元数据和屏幕截图。如果我们编辑了download下来的`.txt`文件,在使用`fastlane`上传app到`iTunes connect`的时候也会将这些内容上传到`iTunes connect`。 95 | 96 | * 输入`n`,不做任何操作,仍然能使用`fastlane`上传app到`App Store`。 97 | 98 | 4. 如果最后出现`fastlane release`,就表示init成功了。 99 | 100 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed3bde0a93494.png) 101 | 102 | 5. 此时项目目录下会多出一个`fastlane`的文件夹。 103 | 104 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed734379c2e01.png) 105 | 106 | 如果`Deliverfile`,`screenshots`和`metadata`目录没被创建,可以运行`deliver init`来创建。 107 | 108 | **在`Deliverfile`文件里,添加`force true`,不然会在上传到`iTunes connect`的时候会弹出一个`Preview.html`网页。** 109 | 110 | ## 使用Gemfile 111 | 112 | 1. 在项目根目录下`touch`一个`Gemfile`文件,添加以下内容 113 | 114 | ``` 115 | source "https://rubygems.org" 116 | gem "fastlane" 117 | ``` 118 | 119 | 2. 执行如下命令: 120 | 121 | ``` 122 | # 安装bundler 123 | sudo gem install bundler 124 | 125 | # 更新 bundle,成功之后会生成一个版本控制的Gemfile.lock文件 126 | [sudo] bundle update 127 | ``` 128 | 129 | 3. 执行命令:`bundle exec fastlane [lane_name]`,执行`lane_name`脚本。这里的`lane_name`是脚本的名称,我们可以理解为函数名,如果我们只执行`bundle exec fastlane`命令,则会有一个让我们选择的地方,选择需要执行的脚本。 130 | 131 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed81d2b12988a.png) 132 | 133 | 会将项目名,`ipa`存放的路径,`app_identifier`等一系列信息打印出来。 134 | 135 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/16170be40bdda6f2.png) 136 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/16170bef5ace5962.png) 137 | `ipa`和`dYSM`文件都存放在项目根目录。 138 | 139 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed7bbe09085cd.png) 140 | 141 | 紧接着会自动上传`metadata`和`ipa`到`iTunes Connect`。 142 | 143 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed8f2edde989c.png) 144 | 145 | 最后会输出每个脚本执行所消耗的时间(s)。 146 | 147 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/160ed91244f83b13.png) 148 | 149 | 150 | ## 进阶 151 | 152 | 如果只是很简单的上传到`iTunes connect`,上面的操作就可以满足。 153 | 154 | 如果我们是多个`target`或者需要配置一些`ITC`上面的内容,则需要进一步的深入。 155 | 156 | ### metadata 157 | 158 | `metadata`是包含应用在`ITC`上面的各种信息,可以使用它配置我们的`ITC`,建议使用`Deliverfile`。 159 | 160 | ### screenshots 161 | 162 | 屏幕截图数据。 163 | 164 | ### [Appfile](https://docs.fastlane.tools/advanced/#appfile) 165 | 166 | 存储App信息,比如`Apple ID`,`bundle ID`等信息。 167 | 168 | ### [Deliverfile](https://docs.fastlane.tools/actions/deliver/#parameters) 169 | 170 | 交付文件。在这个文件里面可以设置`iTunes connect`的所有配置项,例如: 171 | 172 | * `release_notes`,此版本新增内容。 173 | * `copyright`,版权信息。 174 | * `submit_for_review`,上传完成后是否直接提交新版本进行审查。 175 | * `force`,跳过HTML报告文件验证。 176 | * ... 177 | 178 | 179 | 请在设置`release_nores`、`support_url`、`private_url`等配置的时候,采用`hash`的方式写,[国家代码](https://www.cnblogs.com/Mien/archive/2008/08/22/1273950.html),例如: 180 | 181 | ``` 182 | release_notes( 183 | # 中国 184 | 'zh-Hans' => ENV['RELEASE_NOTES'], 185 | # 澳大利亚 186 | 'en-au' => ENV['RELEASE_NOTES_AU'], 187 | # 美国 188 | 'en-us' => ENV['RELEASE_NOTES_US'] 189 | ) 190 | ``` 191 | 192 | ### [Fastfile](https://docs.fastlane.tools/getting-started/ios/screenshots/#use-in-fastfile) 193 | 194 | 自动化脚本配置文件。 195 | 是我们脚本的入口,所有的事件驱动都是在这个文件来调度的。 196 | 197 | ``` 198 | default_platform(:ios) 199 | 200 | platform :ios do 201 | 202 | desc "demo upload_to_app_store" 203 | lane : Archive_TargetA do |options| 204 | scheme = options[:scheme] 205 | date = Time.new.strftime("%Y%m%d-%h%M") 206 | 207 | # export_method 支持 app-store, ad-hoc, package, enterprise, development 208 | gym( 209 | scheme: "#{scheme}", 210 | output_name: "#{scheme}-#{date}.ipa", 211 | clean: true, 212 | export_method: 'app-store', 213 | ) 214 | 215 | # upload_to_app_store 216 | deliver # 当deliverfile为空的时候,同 upload_to_app_store 作用一样 217 | end 218 | end 219 | 220 | ``` 221 | 222 | cd到项目根目录执行命令:`bundle exec fastlane Archive_TargetA scheme:"CDDemo"`,后面的`scheme`是带的参数。 223 | 224 | ### Multi-Target 225 | 226 | 如果我们需要配置多个target进行打包的话,我们可以使用[环境变量](https://docs.fastlane.tools/advanced/#environment-variables),来进行配置。假如我们现在有两个`target`,`targetA` 和 `targetB`,则我们需要创建两个`.env`文件,例如`.env.targetA`,`.env.targetB`,放在`Fastfile`文件同级目录下 227 | 228 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/16130259c16487a4.png) 229 | 230 | 在`.env`文件里面我们可以配置一些不同的内容(非公共),比如`app_identifier`,`release_notes`等等。截图如下: 231 | 232 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/161301cd070af847.png) 233 | 234 | 在`Appfile`,`Deliverfile`,`Fastfile`等文件,我们都可以直接使用`.env`文件里面的内容。 235 | 236 | Appfile 237 | 238 | ``` 239 | # Appfile 240 | 241 | #The bundle identifier of your app 242 | app_identifier ENV['APP_IDENTIFIER'] 243 | 244 | # Your Apple email address 245 | apple_id ENV['APPLE_ID'] 246 | 247 | # Developer Portal Team ID 248 | team_id ENV['TEAM_ID'] 249 | 250 | ``` 251 | 252 | Deliverfile,**请在设置release_nores、support_url、private_url等配置的时候,采用hash的方式写。** 253 | 254 | ``` 255 | # app_identifier 256 | app_identifier ENV['APP_IDENTIFIER'] 257 | 258 | # 用户名,Apple ID电子邮件地址 259 | username ENV['APPLE_ID'] 260 | 261 | # 团队ID 262 | team_id ENV['TEAM_ID'] 263 | 264 | # 团队name 265 | team_name ENV['TEAM_NAME'] 266 | 267 | # copyright 268 | copyright ENV['COPYRIGHT'] 269 | 270 | # 关键字 271 | keywords( 272 | 'zh-Hans' => ENV['KEYWORDS'], 273 | ) 274 | 275 | # 新版本修改记录 276 | release_notes( 277 | # 中国 278 | 'zh-Hans' => ENV['RELEASE_NOTES'], 279 | # 澳大利亚 280 | 'en-au' => ENV['RELEASE_NOTES_AU'], 281 | # 美国 282 | 'en-us' => ENV['RELEASE_NOTES_US'] 283 | ) 284 | 285 | # 支持网址 286 | support_url( 287 | # 中国 288 | 'zh-Hans' => ENV['SUPPORT_URL'], 289 | # 澳大利亚 290 | 'en-au' => ENV['SUPPORT_URL_AU'], 291 | # 美国 292 | 'en-us' => ENV['SUPPORT_URL_US'] 293 | ) 294 | 295 | # 隐私政策网址 国家代码 https://www.cnblogs.com/Mien/archive/2008/08/22/1273950.html 296 | privacy_url( 297 | # 中国 298 | 'zh-Hans' => ENV['PRIVACY_URL'], 299 | # 澳大利亚 300 | 'en-au' => ENV['PRIVACY_URL_AU'], 301 | # 美国 302 | 'en-us' => ENV['PRIVACY_URL_US'] 303 | ) 304 | 305 | # 上传完成后提交新版本进行审查 306 | submit_for_review false 307 | 308 | # 跳过HTML报告文件验证 309 | force true 310 | 311 | # 启用iTC的分阶段发布功能 灰度发布 312 | phased_release true 313 | 314 | # 应用审核小组的联系信息 app 审核信息 315 | app_review_information( 316 | first_name: "xx", 317 | last_name: "xx", 318 | phone_number: "+86 18888888888", 319 | email_address: "xxxx", 320 | demo_user: "test1@test.com", 321 | demo_password: "test123" 322 | ) 323 | 324 | ... 325 | ``` 326 | 327 | Fastfile文件里面使用环境变量,跟上面略有不同。在Fastfile里面,我们需要告诉`lane` 要使用那个`.env`文件,这时候我们需要使用`sh`脚本命令的形式来调用一个lane 后面跟上`--env 环境变量文件名`,此时调用的`lane` 不能声明为`private_lane`,调用方式如下: 328 | 329 | ``` 330 | lane :releaseDemo2 do 331 | # 无参数 332 | sh "fastlane Archive_TargetA --env TargetA" 333 | 334 | # 有参数 335 | sh 'fastlane Archive_TargetA type:\'哈哈哈哈\' --env TargetA' 336 | end 337 | 338 | 339 | 外部直接调用(带参数) 340 | bundle exec fastlane Archive_TargetA type:"haha" --env TargetA 341 | 342 | ``` 343 | 344 | 然后我们在`Archive_TargetA` lane 里面使用`ENV['xx']`方式,读取出来的内容就是从`.env.TargetA`文件读取出来的。同理,`deliver` Action 对应的`DeliverFile`文件里面的内容也是从`.env.TargetA`文件读取出来的。 345 | 346 | ``` 347 | private_lane : Archive_TargetA do |options| 348 | scheme = ENV['SCHEME'] # 这时候读取出来的'scheme'就是'TargetA',从'.env.TargetA'读取出来的 349 | 350 | # export_method 支持 app-store, ad-hoc, package, enterprise, development 351 | gym( 352 | scheme: "#{scheme}", 353 | output_name: "#{scheme}.ipa", 354 | clean: true, 355 | export_method: 'app-store', 356 | ) 357 | 358 | deliver # 这时候deliverfile里面读取的内容就是从'.env.TargetA'文件读取的 359 | end 360 | ``` 361 | 362 | ### lane之间的调用 363 | 364 | 跟我们自己写方法调用一样,例如: 365 | 366 | ``` 367 | desc "打包统一入口" 368 | lane :Archive do |options| 369 | # 如果我们传入的参数'type'是targetA,那么我们就执行Archive_TargetA 这个lane。。。 370 | type = options[:type] 371 | if type == "TargetA" 372 | Archive_TargetA(options) 373 | elsif type == "TargetB" 374 | Archive_TargetB(options) 375 | else 376 | Archive_TargetA(options) 377 | end 378 | end 379 | ``` 380 | 381 | ### 系统级lane 382 | 383 | `fastlane` 默认有 lane。 384 | 385 | * `before_all`,就是在执行一次脚本之前首先执行的代码,我们可以在这里面执行一些公共的东西,比如`git_pull`,`cocoapods`。 386 | 387 | ``` 388 | before_all do 389 | # 检出到 Developer 分支 390 | sh 'git checkout Developer' 391 | git_pull 392 | cocoapods(repo_update: true) 393 | end 394 | ``` 395 | * `after_all `, 成功结束之后,处理共有的后置逻辑。 396 | * `before_each`,每次执行 lane 之前都会执行一次。 397 | * `after_each`,每次执行 lane 之后都会执行一次。 398 | * `error`,在执行上述情况任意环境报错都会中止并执行一次。 399 | 400 | ### 执行顺序 401 | 402 | 执行顺序 | 方法名 | 说明 403 | :---------: | ------------- | --------- 404 | 1 | before_all |在执行 lane 之前只执行一次。 405 | 2 | before_each |每次执行 lane 之前都会执行一次。 406 | 3 | lane |自定义的任务。 407 | 4 | after_each |每次执行 lane 之后都会执行一次。 408 | 5 | after_all |在执行 lane 成功结束之后执行一次。 409 | 6 | error |在执行上述情况任意环境报错都会中止并执行一次。 410 | 411 | ## Error 412 | 413 | * 出现 `Command timed out after 10 seconds on try 1 of 4, trying again...`,在`fastlane`文件开头加上: 414 | 415 | ``` 416 | ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "180" 417 | ENV["FASTLANE_XCODE_LIST_TIMEOUT"] = "180" 418 | ``` 419 | 420 | ## 插件 421 | 422 | * [`versioning`](https://github.com/SiarheiFedartsou/fastlane-plugin-versioning),用来修改`build`版本号和`version`版本号。`Fastlane`内嵌的`actionincrement_build_number`使用的是苹果提供的`agvtool`,agvtool在更改Build的时候会改变所有target的版本号。这时如果你在一个工程里有多个产品的话,每次编译,所有的Build都要加1,最后就不知道高到哪里去了。`fversioning`不仅可以指定target增加Build,而且可以按照「语义化版本」规范增加Version,当然也可以直接设定Version。 423 | * `firim`,直接把`AdHoc`或者`InHouse`打包的ipa上传到`fir.im`,供测试下载。 424 | 425 | ### [自定义插件](https://docs.fastlane.tools/plugins/create-plugin/) 426 | 427 | #### 插件安装格式 428 | 429 | `fastlane add_plugin [name]`,需要到项目根目录下执行。 430 | 431 | `fastlane update_plugins` 插件更新,同上,需要cd到项目根目录下。 432 | 433 | ## 注意 434 | 435 | * 保持打包机器的Xcode 和 证书是最新的。 436 | * 使用脚本命令形式调用的时候不能设置成`private_lane`。 437 | * `private_lane` 表示私有lane,使用`bundle exec fastlane`命令,声明为 `private_lane`的是不是显示出来的,使用脚本命令形式调用的时候不能设置成`private_lane`。 438 | 439 | ## 其它 440 | 441 | 可以直接在`lane`里面执行git命令,例如`sh 'git checkout Developer'`,检出`Developer`分支。 442 | 443 | 由于本人的水平有限,难免会有错误和疏漏,欢迎 [issue](https://github.com/CYBoys/Blogs/issues/new) 指正。如果大家在`Fastlane`的使用上,有更好的案例,也欢迎交流和分享。 444 | 445 | ## 参考文章 446 | * [老邢Thierry的fatlane实战系列](https://www.jianshu.com/u/ea961de5cbe0) 447 | * [iOS-持续交付](https://www.jianshu.com/p/facb9b47b571) 448 | * [Fastlane为iOS带来持续部署](http://www.infoq.com/cn/news/2015/01/fastlane-ios-continuous-deploy) 449 | * [fastlaneTools](https://fastlane.tools/) 450 | * [fastlane文档](https://docs.fastlane.tools/actions/) 451 | * [Fastlane - 移动开发自动化之道](http://blog.csdn.net/ff_wwb/article/details/77530605) 452 | * [Fastlane的黑魔法:一键打包编译上传 AppStore](http://blog.csdn.net/jiang314/article/details/54929471) 453 | * [iOS中使用Fastlane实现自动化打包和发布](https://www.jianshu.com/p/192c09cc8e27) 454 | * [小团队的自动化发布-Fastlane带来的全自动化发布](https://whlsxl.github.io/#to_app_store) 455 | * [fastlane使用说明书](https://www.jianshu.com/p/19ae8cc865b0) 456 | 457 | **[Example](https://github.com/CYBoys/Blogs/tree/master/iOS/Examples/CDDemo)** 458 | 459 | **转载请注明出处!** 460 | 461 | 462 | -------------------------------------------------------------------------------- /iOS/ios-performance-optimization-summary.md: -------------------------------------------------------------------------------- 1 | ## 卡顿产生的原因 2 | 3 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/1623e39ade981eb6.png) 4 | 5 | 在 `VSync` 信号到来后,系统图形服务会通过 `CADisplayLink` 等机制通知 `App`,`App` 主线程开始在 `CPU` 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 `CPU` 会将计算好的内容提交到 `GPU` 去,由 `GPU` 进行变换、合成、渲染。随后 `GPU` 会把渲染结果提交到帧缓冲区去,等待下一次 `VSync` 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 `VSync` 时间内,`CPU` 或者 `GPU` 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。 6 | 7 | 在开发中,`CPU`和`GPU`中任何一个压力过大,都会导致掉帧现象,所以在开发时,也需要分别对`CPU`和`GPU`压力进行评估和优化。 8 | 9 | ## iOS 设备中的 CPU & GPU 10 | 11 | #### CPU 12 | 13 | 加载资源,对象创建,对象调整,对象销毁,布局计算,Autolayout,文本计算,文本渲染,图片的解码, 图像的绘制(Core Graphics)都是在`CPU`上面进行的。 14 | 15 | #### GPU 16 | 17 | `GPU`是一个专门为图形高并发计算而量身定做的处理单元,比`CPU`使用更少的电来完成工作并且`GPU`的浮点计算能力要超出`CPU`很多。 18 | 19 | `GPU`的渲染性能要比`CPU`高效很多,同时对系统的负载和消耗也更低一些,所以在开发中,**我们应该尽量让`CPU`负责主线程的`UI`调动,把图形显示相关的工作交给`GPU`来处理**,当涉及到光栅化等一些工作时,`CPU`也会参与进来,这点在后面再详细描述。 20 | 21 | 相对于`CPU`来说,`GPU`能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合(合成)并渲染,然后输出到屏幕上。通常你所能看到的内容,主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。 22 | 23 | #### CPU 和 GPU 的协作 24 | 25 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/1624403c83678104.png) 26 | 27 | 由上图可知,要在屏幕上显示视图,需要`CPU`和`GPU`一起协作,`CPU`计算好显示的内容提交到`GPU`,`GPU`渲染完成后将结果放到帧缓存区,随后视频控制器会按照 `VSync` 信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。 28 | 29 | #### 缓冲机制 30 | 31 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/162440e866880e84.png) 32 | 33 | `iOS`使用的是双缓冲机制。即`GPU`会预先渲染好一帧放入一个缓冲区内(前帧缓存),让视频控制器读取,当下一帧渲染好后,`GPU`会直接把视频控制器的指针指向第二个缓冲器(后帧缓存)。当你视频控制器已经读完一帧,准备读下一帧的时候,`GPU`会等待显示器的`VSync`信号发出后,前帧缓存和后帧缓存会瞬间切换,后帧缓存会变成新的前帧缓存,同时旧的前帧缓存会变成新的后帧缓存。 34 | 35 | ## 优化方案 36 | 37 | 在`YY`大神的 [iOS 保持界面流畅的技巧](https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/)中详细介绍了 [CPU 资源消耗原因和解决方案](https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/#31)和 [GPU 资源消耗原因和解决方案](https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/#32),这里面包括了开发中的大部分场景,可以帮助我们快速定位卡顿的原因,迅速解决卡顿。 38 | 39 | 下面是一些常见的优化方案! 40 | 41 | #### TableViewCell 复用 42 | 43 | 在`cellForRowAtIndexPath:`回调的时候只创建实例,快速返回`cell`,不绑定数据。在`willDisplayCell: forRowAtIndexPath:`的时候绑定数据(赋值)。 44 | 45 | #### 高度缓存 46 | 47 | 在`tableView`滑动时,会不断调用`heightForRowAtIndexPath:`,当 `cell` 高度需要自适应时,每次回调都要计算高度,会导致 UI 卡顿。为了避免重复无意义的计算,需要缓存高度。 48 | 49 | ##### 怎么缓存? 50 | 51 | * 字典,NSCache。 52 | * [UITableView-FDTemplateLayoutCell](https://github.com/forkingdog/UITableView-FDTemplateLayoutCell) 53 | 54 | ### 视图层级优化 55 | 56 | ##### 不要动态创建视图 57 | 58 | * 在内存可控的前提下,缓存`subview`。 59 | * 善用`hidden`。 60 | 61 | ##### 减少视图层级 62 | 63 | * 减少`subviews`个数,用`layer`绘制元素。 64 | * 少用 `clearColor`,`maskToBounds`,阴影效果等。 65 | 66 | ##### 减少多余的绘制操作 67 | 68 | ### 图片 69 | 70 | * 不要用`JPEG`的图片,应当使用`PNG`图片。 71 | * 子线程预解码(`Decode`),主线程直接渲染。因为当`image`没有`Decode`,直接赋值给`imageView`会进行一个`Decode`操作。 72 | * 优化图片大小,尽量不要动态缩放(`contentMode`)。 73 | * 尽可能将多张图片合成为一张进行显示。 74 | 75 | ### 减少透明 view 76 | 77 | 使用透明`view`会引起`blending`,在`iOS`的图形处理中,`blending`主要指的是混合像素颜色的计算。最直观的例子就是,我们把两个图层叠加在一起,如果第一个图层的透明的,则最终像素的颜色计算需要将第二个图层也考虑进来。这一过程即为`Blending`。 78 | 79 | 会导致`blending`的原因: 80 | 81 | * `UIView`的`alpha` < `1`。 82 | * `UIImageView`的`image`含有`alpha channel`(即使`UIImageView`的`alpha`是`1`,但只要`image`含有透明通道,则仍会导致`blending`)。 83 | 84 | 为什么`blending`会导致性能的损失? 85 | 86 | 原因是很直观的,如果一个图层是不透明的,则系统直接显示该图层的颜色即可。而如果图层是透明的,则会引起更多的计算,因为需要把另一个的图层也包括进来,进行混合后的颜色计算。 87 | 88 | * `opaque`设置为`YES`,减少性能消耗,因为`GPU`将不会做任何合成,而是简单从这个层拷贝。 89 | 90 | ### 减少离屏渲染 91 | 92 | 离屏渲染指的是在图像在绘制到当前屏幕前,需要先进行一次渲染,之后才绘制到当前屏幕。 93 | 94 | `OpenGL`中,`GPU`屏幕渲染有以下两种方式: 95 | 96 | * `On-Screen Rendering`即当前屏幕渲染,指的是`GPU`的渲染操作是在当前用于显示的屏幕缓冲区中进行。 97 | 98 | * `Off-Screen Rendering`即离屏渲染,指的是`GPU`在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。 99 | 100 | 为什么离屏渲染会发生卡顿?主要包括两方面内容: 101 | 102 | * 创建新的缓冲区。 103 | * 上下文切换,离屏渲染的整个过程,需要多次切换上下文环境(`CPU`渲染和`GPU`切换),先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上又需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。 104 | 105 | 设置了以下属性时,都会触发离屏渲染: 106 | 107 | * `layer.shouldRasterize`,光栅化 108 | * `layer.mask`,遮罩 109 | * `layer.allowsGroupOpacity`为`YES`,`layer.opacity`的值小于`1.0` 110 | * `layer.cornerRadius`,并且设置`layer.masksToBounds`为`YES`。可以使用剪切过的图片,或者使用`layer`画来解决。 111 | * `layer.shadows`,(表示相关的shadow开头的属性),使用`shadowPath`代替。 112 | 113 | 两种不同方式来绘制阴影: 114 | 不使用`shadowPath` 115 | 116 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/1624430835219f66.png) 117 | 118 | 使用`shadowPath` 119 | 120 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/1624435303eade8b.png) 121 | 122 | 性能差别,如下图: 123 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/161c09f5e3469ce4.png) 124 | 125 | #### 离屏渲染的优化建议 126 | 127 | * 使用`ShadowPath`指定`layer`阴影效果路径。 128 | * 使用异步进行`layer`渲染(`Facebook`开源的异步绘制框架`AsyncDisplayKit`)。 129 | * 设置`layer`的`opaque`值为`YES`,减少复杂图层合成。 130 | * 尽量使用不包含透明(`alpha`)通道的图片资源。 131 | * 尽量设置`layer`的大小值为整形值。 132 | * 直接让美工把图片切成圆角进行显示,这是效率最高的一种方案。 133 | * 很多情况下用户上传图片进行显示,可以在客户端处理圆角。 134 | * 使用代码手动生成圆角`image`设置到要显示的`View`上,利用`UIBezierPath`(`Core Graphics`框架)画出来圆角图片。 135 | 136 | ### 合理使用光栅化 shouldRasterize 137 | 138 | 光栅化是把`GPU`的操作转到`CPU`上,生成位图缓存,直接读取复用。 139 | 140 | ##### 优点: 141 | 142 | * `CALayer`会被光栅化为`bitmap`,`shadows`、`cornerRadius`等效果会被缓存。 143 | 144 | ##### 缺点: 145 | 146 | * 更新已经光栅化的`layer`,会造成离屏渲染。 147 | * `bitmap`超过`100ms`没有使用就会移除。 148 | * 受系统限制,缓存的大小为 2.5X Screen Size。 149 | 150 | **`shouldRasterize` 适合静态页面显示,动态页面会增加开销。如果设置了`shouldRasterize`为 `YES`,那也要记住设置`rasterizationScale`为`contentsScale`。** 151 | 152 | ### 异步渲染 153 | 154 | 在子线程绘制,主线程渲染。例如 [VVeboTableViewDemo](https://github.com/johnil/VVeboTableViewDemo) 155 | 156 | ![](https://raw.githubusercontent.com/CYBoys/Blogs/master/Pic/ios/1618448a7403100f.png) 157 | 158 | ## 理性使用`-drawRect:` 159 | 160 | 大家或许感到奇怪,有不少开发者在发有关性能优化的博客当中指出使用`-drawRect:`来优化性能。但是我这里不太建议大家未经思考的使用`-drawRect:`方法。原因如下: 161 | 162 | 当你使用`UIImageView`在加载一个视图的时候,这个视图虽然依然有`CALayer`,但是却没有申请到一个后备的存储,取而代之的是使用一个使用屏幕外渲染,将`CGImageRef`作为内容,并用渲染服务将图片数据绘制到帧的缓冲区,就是显示到屏幕上,当我们滚动视图的时候,这个视图将会重新加载,浪费性能。所以对于使用`-drawRect:`方法,更倾向于使用`CALayer`来绘制图层。因为使用`CALayer`的`-drawInContext:`,`Core Animation`将会为这个图层申请一个后备存储,用来保存那些方法绘制进来的位图。那些方法内的代码将会运行在 `CPU`上,结果将会被上传到`GPU`。这样做的性能更为好些。 163 | 164 | 静态界面建议使用`-drawRect:`的方式,动态页面不建议。 165 | 166 | ### 按需加载 167 | 168 | * 局部刷新,刷新一个`cell`就能解决的,坚决不刷新整个 `section` 或者整个`tableView`,刷新最小单元元素。 169 | * 利用[`runloop`](https://github.com/diwu/RunLoopWorkDistribution)提高滑动流畅性,在滑动停止的时候再加载内容,像那种一闪而过的(快速滑动),就没有必要加载,可以使用默认的占位符填充内容。 170 | 171 | ## [关于性能测试](https://github.com/100mango/zen/blob/master/WWDC%E5%BF%83%E5%BE%97%EF%BC%9AAdvanced%20Graphics%20and%20Animations%20for%20iOS%20Apps/Advanced%20Graphics%20and%20Animations%20for%20iOS%20Apps.md#%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7) 172 | 173 | 在出现图像性能问题,滑动,动画不够流畅之后,我们首先要做的就是定位出问题的所在。而这个过程并不是只靠经验和穷举法探索,我们应该用有脉络,有顺序的科学的手段进行探索。 174 | 175 | 首先,我们要有一个定位问题的模式。我们可以按照这样的顺序来逐步定位,发现问题。 176 | 177 | 1. 定位帧率,为了给用户流畅的感受,我们需要保持帧率在`60`帧左右。当遇到问题后,我们首先检查一下帧率是否保持在`60`帧。 178 | 2. 定位瓶颈,究竟是`CPU`还是`GPU`。我们希望占用率越少越好,一是为了流畅性,二也节省了电力。 179 | 3. 检查有没有做无必要的`CPU`渲染,例如有些地方我们重写了`drawRect:`,而其实是我们不需要也不应该的。我们希望`GPU`负责更多的工作。 180 | 4. 检查有没有过多的离屏渲染,这会耗费`GPU`的资源,像前面已经分析的到的。离屏渲染会导致`GPU`需要不断地`onScreen`和`offscreen`进行上下文切换。我们希望有更少的离屏渲染。 181 | 5. 检查我们有无过多的`Blending`,`GPU`渲染一个不透明的图层更省资源。 182 | 6. 检查图片的格式是否为常用格式,大小是否正常。如果一个图片格式不被`GPU`所支持,则只能通过`CPU`来渲染。一般我们在`iOS`开发中都应该用`PNG`格式,之前阅读过的一些资料也有指出苹果特意为`PNG`格式做了渲染和压缩算法上的优化。 183 | 7. 检查是否有耗费资源多的`View`或效果,我们需要合理有节制的使用。 184 | 8. 最后,我们需要检查在我们`View`层级中是否有不正确的地方。例如有时我们不断的添加或移除`View`,有时就会在不经意间导致`bug`的发生。 185 | 186 | ##### 测试工具: 187 | 188 | * `Core Animation`,`Instruments`里的图形性能问题的测试工具。 189 | * `view debugging`,Xcode 自带的,视图层级。 190 | * `reveal`,视图层级。 191 | 192 | ## 参考文章 193 | 194 | * [绘制像素到屏幕上](https://objccn.io/issue-3-1/) 195 | * [iOS图形原理与离屏渲染](http://sonnewilling.com/blog/2016/10/19/iostu-xing-yuan-li-yu-chi-ping-xuan-ran/#anchor1.1),在1.4.1中,`这也是为什么 CALayer 有一个叫做 opaque 的属性了。如果这个属性为 NO,GPU 将不会做任何合成,而是简单从这个层拷贝,不需要考虑它下方的任何东西(因为都被它遮挡住了)。`中的 `opaque`属性为`NO`,`GPU`将不会做任何合成,这句话时错误的,应该是为`YES`,`GPU`才不会做任何合成。 196 | * [iOS 保持界面流畅的技巧](https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/) 197 | * [Advanced Graphics and Animations for iOS Apps(session 419)](https://github.com/100mango/zen/blob/master/WWDC%E5%BF%83%E5%BE%97%EF%BC%9AAdvanced%20Graphics%20and%20Animations%20for%20iOS%20Apps/Advanced%20Graphics%20and%20Animations%20for%20iOS%20Apps.md) 198 | * [使用 ASDK 性能调优 - 提升 iOS 界面的渲染性能](https://draveness.me/asdk-rendering) 199 | * [Designing for iOS: Graphics & Performance](https://robots.thoughtbot.com/designing-for-ios-graphics-performance) 200 | * [iOS离屏渲染之优化分析](https://www.jianshu.com/p/52c72f18e142) 201 | * [iOS视图渲染以及性能优化总结](https://www.jianshu.com/p/b29c682679c4?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation) 202 | * [iOS 离屏渲染](https://www.jianshu.com/p/1c80ccc01919?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation) 203 | * [深刻理解移动端优化之离屏渲染](https://www.jianshu.com/p/d74398c50fe1?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation) 204 | * [iOS 流畅度性能优化、CPU、GPU、离屏渲染](https://www.jianshu.com/p/d27323c18790?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation) 205 | * [iOS 图形性能优化锦集](http://www.bijishequ.com/detail/268411) 206 | * [离屏渲染优化详解:实例示范+性能测试](https://www.jianshu.com/p/ca51c9d3575b) 207 | 208 | 如有内容错误,欢迎 [issue](https://github.com/CYBoys/Blogs/issues/new) 指正。 209 | 210 | **转载请注明出处!** 211 | 212 | 213 | --------------------------------------------------------------------------------