├── REDME_CN.md ├── README.md ├── ios_ipa_analyse.py └── LICENSE /REDME_CN.md: -------------------------------------------------------------------------------- 1 | # LinkMap解析工具:检查每个类占用大小,可以输出到txt文本 2 | 3 | ## 概述 4 | 5 | 一个大型的项目,只是代码段就有可能超过100M,算上armv7和arm64架构,就会超过200M。 这时候检查到底是哪个类、哪个第三方库占用了太多空间,就显得尤为重要。 6 | 7 | 这个工具是专为用来分析项目的LinkMap文件,得出每个类或者库所占用的空间大小(代码段+数据段),方便开发者快速定位需要优化的类或静态库。 8 | 9 | 这个工具使用Python开发,可以部署到构建机平台,每次构建的时候可以输出包大小差异,方便开发者关注包的大小 10 | 11 | ## 使用说明 12 | 13 | ### 1.安装Python环境 14 | 15 | iOS_Ipa_Analyse是一个Python脚本,运行该脚本需要开发者的机器有Python环境,不过我们iOS的构建机一般是Mac,所以可以忽略。目前我使用的Python版本是2.7 16 | 17 | ### 2.运行工具 18 | 19 | 该工具支持分析一个link map文件和比较两个link map文件,运行的命令分别为: 20 | 21 | #### 1、分析一个 link map文件 22 | 23 | ```shell 24 | python ios_ipa_analyse.py $map_link_file_path 25 | ``` 26 | 27 | #### 输出结果: 28 | 29 | ```shell 30 | AppDelegate.o 0.01K 31 | ViewController.o 0.00K 32 | main.o 0.00K 33 | libobjc.tbd 0.00K 34 | linker synthesized 0.00K 35 | Foundation.tbd 0.00K 36 | UIKit.tbd 0.00K 37 | 总体积: 38 | ``` 39 | #### 2.比较两个link map文件 40 | 41 | ```shell 42 | python ios_ipa_analyse.py $map_link_file_path $target_map_link_file_path 43 | ``` 44 | 45 | LinkMapParser会分析两个map link文件,然后比较各个模块的体积是否有变化,最后列出体积变大的模块。 46 | 47 | #### 输出结果类似于: 48 | 49 | ```shell 50 | ================================================================================ 51 | xxx/link_map_result.txt各模块体积汇总 52 | ================================================================================ 53 | Creating Result File : xxx/link_map_result.txt 54 | AppDelegate.o 0.01M 55 | ViewController.o 0.00M 56 | main.o 0.00M 57 | libobjc.tbd 0.00M 58 | linker synthesized 0.00M 59 | Foundation.tbd 0.00M 60 | UIKit.tbd 0.00M 61 | 总体积: 0.01M 62 | 63 | ================================================================================ 64 | xxx/target_link_map_result.txt各模块体积汇总 65 | ================================================================================ 66 | Creating Result File : xxx/target_link_map_result.txt 67 | AppDelegate.o 0.64K 68 | ViewController.o 0.00K 69 | main.o 0.00K 70 | libobjc.tbd 0.00K 71 | linker synthesized 0.00K 72 | Foundation.tbd 0.00K 73 | UIKit.tbd 0.00K 74 | 总体积: 0.64M 75 | 76 | 77 | ================================================================================ 78 | 比较结果 79 | ================================================================================ 80 | 模块名称 基线大小 目标大小 是否新模块 81 | AppDelegate.o 0.01M 0.64M 82 | ``` 83 | 84 | ## 如何获得LinkMap文件 85 | 86 | 1.在XCode中开启编译选项Write Link Map File 87 | 88 | XCode -> Project -> Build Settings -> 把Write Link Map File选项设为yes,并指定好linkMap的存储位置 89 | 90 | 2.工程编译完成后,在编译目录里找到Link Map文件(txt类型) 91 | 92 | 默认的文件地址:~/Library/Developer/Xcode/DerivedData/XXX-xxxxxxxxxxxxx/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build 93 | 94 | ## 感谢 95 | 96 | 感谢这两位开发者,在我寻找思路和实现方案的时候提供了很多参考,甚至有些代码是使用了他们的代码 97 | 98 | https://github.com/huanxsd/LinkMap 99 | 100 | https://github.com/zgzczzw/LinkMapParser 101 | 102 | 103 | ## 联系我 104 | 105 | 如果有问题欢迎联系我 coderlawrence@163.com 106 | 107 | ## 最后 108 | 109 | 如果喜欢,请顺手我一个star吧~ 110 | 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Link Map Parser: Tool for Checking Class Size 2 | 3 | ## Overview 4 | 5 | In a large project, just the code section alone can exceed 100M, and with both armv7 and arm64 architectures, it can go over 200M. At this point, it becomes crucial to determine which classes or third-party libraries are taking up too much space. 6 | 7 | This tool is designed to analyze a project's Link Map file, determining the space each class or library occupies (code section + data section). This makes it easy for developers to quickly identify classes or static libraries that need optimization. 8 | 9 | This tool is developed in Python and can be deployed on build machine platforms. It can output package size differences each time a build occurs, enabling developers to focus on package size. 10 | 11 | ## Instructions 12 | 13 | ### 1. Install Python Environment 14 | 15 | iOS_Ipa_Analyse is a Python script that requires Python environment on the developer's machine to run. Since our iOS build machines are generally Macs, this requirement can be ignored. Currently, I am using Python version 2.7. 16 | 17 | ### 2. Running the Tool 18 | 19 | This tool supports analyzing a single link map file and comparing two link map files. The commands to run are as follows: 20 | 21 | #### 1. Analyzing a Single Link Map File 22 | 23 | ```shell 24 | python ios_ipa_analyse.py $map_link_file_path 25 | ``` 26 | ##### Output: 27 | 28 | ```shell 29 | AppDelegate.o 0.01K 30 | ViewController.o 0.00K 31 | main.o 0.00K 32 | libobjc.tbd 0.00K 33 | linker synthesized 0.00K 34 | Foundation.tbd 0.00K 35 | UIKit.tbd 0.00K 36 | 总体积: 37 | ``` 38 | #### 2. Comparing Two Link Map Files 39 | 40 | ```shell 41 | python ios_ipa_analyse.py $map_link_file_path $target_map_link_file_path 42 | ``` 43 | 44 | Link Map Parser analyzes two map link files, compares the sizes of various modules for changes, and lists out modules that have increased in size. 45 | 46 | ##### Output is similar to: 47 | 48 | ```shell 49 | ================================================================================ 50 | xxx/link_map_result.txt各模块体积汇总 51 | ================================================================================ 52 | Creating Result File : xxx/link_map_result.txt 53 | AppDelegate.o 0.01M 54 | ViewController.o 0.00M 55 | main.o 0.00M 56 | libobjc.tbd 0.00M 57 | linker synthesized 0.00M 58 | Foundation.tbd 0.00M 59 | UIKit.tbd 0.00M 60 | 总体积: 0.01M 61 | 62 | ================================================================================ 63 | xxx/target_link_map_result.txt各模块体积汇总 64 | ================================================================================ 65 | Creating Result File : xxx/target_link_map_result.txt 66 | AppDelegate.o 0.64K 67 | ViewController.o 0.00K 68 | main.o 0.00K 69 | libobjc.tbd 0.00K 70 | linker synthesized 0.00K 71 | Foundation.tbd 0.00K 72 | UIKit.tbd 0.00K 73 | 总体积: 0.64M 74 | 75 | 76 | ================================================================================ 77 | 比较结果 78 | ================================================================================ 79 | 模块名称 基线大小 目标大小 是否新模块 80 | AppDelegate.o 0.01M 0.64M 81 | ``` 82 | ## Obtaining the Link Map File 83 | 84 | 1、Enable the "Write Link Map File" compilation option in Xcode: 85 | 86 | XCode -> Project -> Build Settings -> Set "Write Link Map File" to yes and specify the linkMap storage location. 87 | 88 | 2、After the project compiles, find the Link Map file (in txt format) in the build directory: 89 | 90 | Default file location: ~/Library/Developer/Xcode/DerivedData/XXX-xxxxxxxxxxxxx/Build/Intermediates/XXX.build/Debug-iphoneos/XXX.build 91 | 92 | ## Acknowledgments 93 | 94 | Special thanks to these developers for providing valuable references and even code snippets during my search for solutions and implementation: 95 | 96 | - https://github.com/huanxsd/LinkMap 97 | - https://github.com/zgzczzw/LinkMapParser 98 | 99 | ## Contact Me 100 | 101 | If you have any questions, feel free to reach out to me at coderlawrence@163.com. 102 | 103 | ## Conclusion 104 | 105 | If you find this helpful, please consider giving it a star! 106 | 107 | -------------------------------------------------------------------------------- /ios_ipa_analyse.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | # author: Lawrence 3 | # mail: coderlawrence@163.com 4 | 5 | import sys 6 | import os 7 | 8 | 9 | # 解析 link map 文件,并且保存到map 10 | def link_map_file_parser(link_map_file_tmp): 11 | reach_files = 0 12 | reach_sections = 0 13 | reach_symbols = 0 14 | size_map = {} 15 | while 1: 16 | line = link_map_file_tmp.readline() 17 | if not line: 18 | break 19 | if line.startswith("#"): 20 | if line.startswith("# Object files:"): 21 | reach_files = 1 22 | pass 23 | if line.startswith("# Sections:"): 24 | reach_sections = 1 25 | pass 26 | if line.startswith("# Symbols:"): 27 | reach_symbols = 1 28 | pass 29 | pass 30 | else: 31 | if reach_files == 1 and reach_sections == 0 and reach_symbols == 0: 32 | index = line.find("]") 33 | if index != -1: 34 | symbol = {"file": line[index + 2: -1]} 35 | key = int(line[1: index]) 36 | size_map[key] = symbol 37 | pass 38 | elif reach_files == 1 and reach_sections == 1 and reach_symbols == 0: 39 | pass 40 | elif reach_files == 1 and reach_sections == 1 and reach_symbols == 1: 41 | symbol_array = line.split("\t") 42 | if len(symbol_array) == 3: 43 | file_key_and_name = symbol_array[2] 44 | size = int(symbol_array[1], 16) 45 | index = file_key_and_name.find("]") 46 | if index != -1: 47 | key = file_key_and_name[1:index] 48 | key = int(key) 49 | symbol = size_map[key] 50 | if symbol: 51 | if "size" in symbol: 52 | symbol["size"] += size 53 | else: 54 | symbol["size"] = size 55 | pass 56 | else: 57 | print("invalid #3") 58 | pass 59 | return size_map 60 | 61 | 62 | # 输出 link map 解析到目标路径下 63 | def write_link_map_to_result_file(size_map, link_mpa_file_path, link_map_result_file_path, analyse_group): 64 | total_size = 0 65 | a_file_map = {} 66 | for key in size_map: 67 | symbol = size_map[key] 68 | if "size" in symbol: 69 | # 是否需要分组统计,如果不需要会把.a or framework库里面的类大小输出 70 | if not analyse_group: 71 | total_size += symbol["size"] 72 | o_file_name = symbol["file"].split("/")[-1] 73 | if o_file_name in a_file_map: 74 | a_file_map[o_file_name] += symbol["size"] 75 | pass 76 | else: 77 | a_file_map[o_file_name] = symbol["size"] 78 | pass 79 | pass 80 | else: 81 | total_size += symbol["size"] 82 | o_file_name = symbol["file"].split("/")[-1] 83 | a_file_name = o_file_name.split("(")[0] 84 | if a_file_name in a_file_map: 85 | a_file_map[a_file_name] += symbol["size"] 86 | pass 87 | else: 88 | a_file_map[a_file_name] = symbol["size"] 89 | pass 90 | pass 91 | else: 92 | print("WARN : some error occurred for key"), 93 | print(key) 94 | # 根据map对文件进行排序 95 | a_file_sorted_list = sorted(a_file_map.items(), key=lambda x: x[1], reverse=True) 96 | print("%s" % "=".ljust(80, '=')) 97 | print("%s" % (link_mpa_file_path + "各模块体积汇总").center(87)) 98 | print("%s" % "=".ljust(80, '=')) 99 | if os.path.exists(link_map_result_file_path): 100 | os.remove(link_map_result_file_path) 101 | pass 102 | print("Creating Result File : %s" % link_map_result_file_path) 103 | output_file = open(link_map_result_file_path, "w") 104 | print("%s \t\t\t%s\n\n" % ("文件:".ljust(53), "大小:")) 105 | output_file.write("%s \t\t\t%s\n\n" % ("文件:".ljust(53), "大小:")) 106 | for item in a_file_sorted_list: 107 | # 判断文件大小 108 | if item[1] / 1024.0 / 1024.0 > 1: 109 | print("%s%.2fM" % (item[0].ljust(80), item[1] / 1024.0 / 1024.0)) 110 | output_file.write("%s \t\t\t%.2fM\n" % (item[0].ljust(55), item[1] / 1024.0 / 1024.0)) 111 | pass 112 | else: 113 | print("%s%.2fK" % (item[0].ljust(55), item[1] / 1024.0)) 114 | output_file.write("%s \t\t\t%.2fK\n" % (item[0].ljust(55), item[1] / 1024.0)) 115 | pass 116 | pass 117 | print("%s%.2fM" % ("总体积:".ljust(53), total_size / 1024.0 / 1024.0)) 118 | print("\n\n\n\n\n") 119 | output_file.write("%s%.2fM" % ("总体积:".ljust(53), total_size / 1024.0 / 1024.0)) 120 | output_file.close() 121 | 122 | 123 | # 检查linkMap是否合法 124 | def check_link_map_content(link_map_content): 125 | obj_file_tag_index = link_map_content.find("# Object files:") 126 | sub_obj_file_symbol_str = link_map_content[obj_file_tag_index + 15:] 127 | symbols_index = sub_obj_file_symbol_str.find("# Symbols:") 128 | if obj_file_tag_index == -1 or symbols_index == -1 or link_map_content.find("# Path:") == -1: 129 | return False 130 | return True 131 | 132 | 133 | # 解析link map 文件并且输出到指定目录下 134 | def red_link_map_file(base_link_map_file_path, link_map_result_file_path): 135 | try: 136 | link_map_file = open(base_link_map_file_path, 'r', encoding='utf-8', errors='ignore') 137 | except IOError: 138 | print("read link map file " + base_link_map_file_path + " failed!") 139 | return 140 | else: 141 | try: 142 | content = link_map_file.read() 143 | except IOError: 144 | print("read link map file " + base_link_map_file_path + " failed!") 145 | else: 146 | # 检查link map 的合法性 147 | if not check_link_map_content(content): 148 | print("the content of file " + base_link_map_file_path + " is invalid") 149 | pass 150 | link_map_file_tmp = open(base_link_map_file_path, 'r', encoding='utf-8', errors='ignore') 151 | size_map = link_map_file_parser(link_map_file_tmp) 152 | if not size_map: 153 | print("the link map parser " + base_link_map_file_path + " failed!") 154 | pass 155 | # analyse_group 参数需要传入,目前是写死的 156 | write_link_map_to_result_file(size_map, base_link_map_file_path, link_map_result_file_path, False) 157 | link_map_file_tmp.close() 158 | finally: 159 | link_map_file.close() 160 | 161 | 162 | def parse_result_file(result_file_path): 163 | base_bundle_list = [] 164 | result_file = open(result_file_path) 165 | # 逐行读取文件 166 | while 1: 167 | line = result_file.readline() 168 | if not line: 169 | break 170 | bundle_and_size = line.split() 171 | if len(bundle_and_size) == 2 and line.find(":") == -1: 172 | bundle_and_size_map = {"name": bundle_and_size[0], "size": bundle_and_size[1]} 173 | base_bundle_list += [bundle_and_size_map] 174 | pass 175 | return base_bundle_list 176 | 177 | 178 | # 比较两个 link map的差异 179 | def compare_link_map(base_bundle_list, target_bundle_list): 180 | print("%s" % "=".ljust(80, '=')) 181 | print("%s" % "比较结果".center(84)) 182 | print("%s" % "=".ljust(80, '=')) 183 | print("%s%s%s%s" % ("模块名称".ljust(54), "基线大小".ljust(14), "目标大小".ljust(14), "是否新模块".ljust(14))) 184 | for target_bundle_map in target_bundle_list: 185 | target_name = target_bundle_map["name"] 186 | target_size = target_bundle_map["size"] 187 | # 需要考虑单位不一致问题 188 | if target_size.find("M") != -1: 189 | # 单位对齐 190 | target_size_value = float(target_size.split("M")[0]) * 1024 191 | else: 192 | target_size_value = float(target_size.split("K")[0]) 193 | pass 194 | has_bundle_in_base = 0 195 | base_size_value = 0 196 | for base_bundle_map in base_bundle_list: 197 | base_name = base_bundle_map["name"] 198 | if base_name == target_name: 199 | base_size = base_bundle_map["size"] 200 | if base_size.find("M") != -1: 201 | # 单位对齐 202 | base_size_value = float(base_size.split("M")[0]) * 1024 203 | else: 204 | base_size_value = float(base_size.split("K")[0]) 205 | pass 206 | has_bundle_in_base = 1 207 | if base_size_value < target_size_value: 208 | print("%s%s%s" % (target_name.ljust(50), str("%.2fK" % base_size_value).ljust(10), str("%.2fK" % target_size_value).ljust(10))) 209 | pass 210 | break 211 | pass 212 | if has_bundle_in_base == 0: 213 | print("%s%s%s%s" % (target_name.ljust(50), str("%.2fK" % base_size_value).ljust(10), str("%.2fK" % target_size_value).ljust(10), "Y".center(10))) 214 | pass 215 | pass 216 | 217 | 218 | def print_help(): 219 | print("%s" % "=".ljust(80, '=')) 220 | print("%s%s\n" % ("".ljust(10), "Link Map 文件分析工具".ljust(80))) 221 | print("%s%s\n" % ("".ljust(10), "- Usage : python ios_ipa_analyse.py arg1 ".ljust(80))) 222 | print("%s%s" % ("".ljust(10), "- arg1 :基准LinkMap文件路径".ljust(80))) 223 | print("%s%s\n" % ("".ljust(10), "- arg2 :待比较LinkMap文件路径".ljust(80))) 224 | print("%s%s" % ("".ljust(10), "备注:参数2为空时,只输出基准LinkMap分析结果".ljust(80))) 225 | print("%s" % "=".ljust(80, '=')) 226 | 227 | 228 | def clean_result_file(file_name): 229 | if os.path.exists(file_name): 230 | os.remove(file_name) 231 | pass 232 | 233 | 234 | def ios_ipa_analyse(): 235 | if len(sys.argv) == 2: 236 | need_compare = 0 237 | pass 238 | elif len(sys.argv) == 3: 239 | need_compare = 1 240 | pass 241 | else: 242 | print_help() 243 | return 244 | pass 245 | 246 | base_map_link_file = sys.argv[1] 247 | output_file_path = os.path.dirname(base_map_link_file) 248 | if output_file_path: 249 | base_output_file = output_file_path + "/link_map_result.txt" 250 | pass 251 | else: 252 | base_output_file = "link_map_result.txt" 253 | pass 254 | red_link_map_file(base_map_link_file, base_output_file) 255 | 256 | if need_compare == 1: 257 | target_map_link_file = sys.argv[2] 258 | output_file_path = os.path.dirname(target_map_link_file) 259 | if output_file_path: 260 | target_output_file = output_file_path + "/target_link_map_result.txt" 261 | pass 262 | else: 263 | target_output_file = "target_link_map_result.txt" 264 | pass 265 | red_link_map_file(target_map_link_file, target_output_file) 266 | 267 | base_bundle_list = parse_result_file(base_output_file) 268 | target_bundle_list = parse_result_file(target_output_file) 269 | 270 | compare_link_map(base_bundle_list, target_bundle_list) 271 | 272 | # clean_result_file(base_output_file) 273 | # clean_result_file(target_output_file) 274 | 275 | 276 | if __name__ == '__main__': 277 | ios_ipa_analyse() 278 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------