27 |
30 |
31 |
32 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/binsize.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from __future__ import print_function
4 | from elfsize import add_node, output_to_file, default_op, default_datafile, repo_root
5 | from collections import OrderedDict
6 | from os import path
7 | import sys
8 |
9 | def main(output):
10 | root = OrderedDict({"name": "mbed", "children": []})
11 |
12 | # cycle through stdin
13 | for line in sys.stdin:
14 | # pass through to stdout
15 | print(line.strip())
16 |
17 | # filter for only relavent lines
18 | if line.startswith('|') and "Subtotals" not in line and '| Module' not in line and "---" not in line:
19 | # unpack the line
20 | l = line.strip().split('|')
21 | l = [x.strip() for x in l if x != '']
22 | obj_path, text_size, data_size, bss_size = l
23 |
24 | # trim the [lib] prefix
25 | if obj_path.startswith("[lib]"):
26 | obj_path = obj_path[6:]
27 |
28 | # generate 3 nodes for each file: .text, .data, .bss
29 | nodes = []
30 |
31 | text_size = int(text_size.split("(")[0])
32 | if text_size > 0:
33 | nodes.append((path.join('.text', obj_path), text_size))
34 |
35 | data_size = int(data_size.split("(")[0])
36 | if data_size > 0:
37 | nodes.append((path.join('.data', obj_path), data_size))
38 |
39 | bss_size = int(bss_size.split("(")[0])
40 | if bss_size > 0:
41 | nodes.append((path.join('.bss', obj_path), bss_size))
42 |
43 | for node in nodes:
44 | add_node(root, node[0], node[1])
45 |
46 | output_to_file(output, root)
47 |
48 | if __name__ == '__main__':
49 | import argparse, webbrowser
50 |
51 | parser = argparse.ArgumentParser(
52 | description='Analyse mbed compile output from stdin and generate a json data file for visualisation')
53 |
54 | def output_arg(s):
55 | if path.isdir(s):
56 | s = path.join(s, default_datafile)
57 | return open(s, "wb")
58 |
59 | # specify arguments
60 | parser.add_argument('-o', '--output', type = output_arg,
61 | help = 'path of output json, defaults to {}, default filename \
62 | to {} if a folder is specified'.format(default_op, default_datafile),
63 | default = default_op)
64 | parser.add_argument('-b', '--browser', action='store_true',
65 | help = 'launch the pie chart visualisation in a browser')
66 |
67 | # get and validate arguments
68 | args = parser.parse_args()
69 |
70 | # parse input and write to output
71 | main(args.output)
72 |
73 | # close output file
74 | output_fn = path.abspath(args.output.name)
75 | args.output.close()
76 |
77 | print("[INFO] data written to", output_fn)
78 |
79 | if args.browser:
80 | uri = "file://" + path.join(repo_root, "index.html")
81 | print("[INFO] opening in browser", uri)
82 | webbrowser.open(uri, new=2)
83 |
--------------------------------------------------------------------------------
/elfsize.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from __future__ import print_function
4 | from subprocess import check_output
5 | from collections import OrderedDict
6 | from os import path
7 | import re, os, json
8 |
9 | # arm-none-eabi-nm needs to be in the environment path
10 | nm = "arm-none-eabi-nm"
11 | nm_opts = "-l -S -C -f sysv"
12 | default_datafile = "data-flare.js"
13 | repo_root = path.dirname(path.abspath(__file__))
14 | default_op = path.join(repo_root, "html", default_datafile)
15 |
16 | def add_node(root, node_path, node_size):
17 | node = root
18 | added = 0
19 | for p in node_path.split(os.sep):
20 | children = node["children"]
21 | if p not in [x['name'] for x in children]:
22 | add = {"name": p, "children": []}
23 | children.append(add)
24 | added = 1
25 | node = children[-1]
26 | else:
27 | p_index = [x['name'] for x in children].index(p)
28 | node = children[p_index]
29 |
30 | if added:
31 | node.pop("children")
32 | node['size'] = node_size
33 |
34 | def output_to_file(fd, root):
35 | # write dict as json to output file
36 | s = json.dumps(dict(root), indent=4)
37 | fd.seek(0)
38 | fd.write(("var mbed_map = ").encode())
39 | fd.write(s.encode())
40 | fd.truncate()
41 |
42 | def main(binaries, output):
43 | op = ""
44 | for binary in binaries:
45 | # run nm command on binary
46 | cmd = [nm] + nm_opts.split() + [binary]
47 | print(" ".join(cmd))
48 | op += check_output(cmd).decode().strip()
49 |
50 | # parse output and store in dict
51 | root = OrderedDict({"name": "mbed", "children": []})
52 | for line in op.split('\n'):
53 | if '|' in line:
54 | l = [x.strip() for x in re.split("\||\t", line.strip())]
55 | if len(l) == 7:
56 | l.append('./libc')
57 |
58 | l[7] = path.join(l[6], path.relpath(path.abspath(l[7]), os.getcwd()).split(':')[0], l[0])
59 |
60 | try:
61 | node_size = int(l[4],16)
62 | except ValueError:
63 | continue;
64 | node_path = l[7]
65 | add_node(root, node_path, node_size)
66 |
67 | output_to_file(output, root)
68 |
69 | if __name__ == '__main__':
70 | import argparse, webbrowser
71 |
72 | parser = argparse.ArgumentParser(
73 | description='Analyse binary built by gcc and generate json containing binary size information')
74 |
75 | def output_arg(s):
76 | if path.isdir(s):
77 | s = path.join(s, default_datafile)
78 | return open(s, "wb")
79 |
80 | # specify arguments
81 | parser.add_argument('-i', '--binary', type = str, required = True, nargs = '*',
82 | help = 'path to the binary. You can also specify multiple binaries: -i ')
83 | parser.add_argument('-o', '--output', type = output_arg,
84 | help = 'path of output json, defaults to {}, default filename \
85 | to {} if a folder is specified'.format(default_op, default_datafile),
86 | default = default_op)
87 | parser.add_argument('-b', '--browser', action='store_true',
88 | help = 'launch the pie chart visualisation in a browser')
89 |
90 | # get and validate arguments
91 | args = parser.parse_args()
92 |
93 | # parse input and write to output
94 | main(args.binary, args.output)
95 |
96 | # close output file
97 | output_fn = path.abspath(args.output.name)
98 | args.output.close()
99 |
100 | print("[INFO] data written to", output_fn)
101 |
102 | if args.browser:
103 | uri = "file://" + path.join(repo_root, "index.html")
104 | print("[INFO] opening in browser", uri)
105 | webbrowser.open(uri, new=2)
106 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Binary Size Analysis
2 | This repository is used to generate interactive linker statistics. Please have a look at our [interactive example](https://armmbed.github.io/mbed-os-linker-report/).
3 |
4 | ## Installation
5 | Install `arm-none-eabi-gcc` and add to your path. This will install `arm-none-eabi-nm`. The analysis tool require the executable `arm-none-eabi-nm` to be present in your environemntal PATH.
6 |
7 | Then clone the tool to a local directory
8 | ```bash
9 | git clone https://github.com/ARMmbed/mbed-os-linker-report
10 | ```
11 |
12 | ## Running the Analysis
13 |
14 | ### File level analysis - any compiler, any compiler profile
15 |
16 | If you want to know how much each file contributes to the final size of the binary mbed-cli provides these statistics at compile time:
17 | ```
18 | > mbed compile -m K64F -t GCC_ARM --stats-depth=100
19 | Building project mbed-os-example-blinky (K64F, GCC_ARM)
20 | Scan: .
21 | Scan: mbed
22 | Scan: env
23 | Scan: FEATURE_LWIP
24 | Scan: FEATURE_STORAGE
25 | ...
26 | +--------------------------------------------------------------------------------------------------------------------+-------+-------+------+
27 | | Module | .text | .data | .bss |
28 | +--------------------------------------------------------------------------------------------------------------------+-------+-------+------+
29 | | [fill] | 86 | 4 | 2369 |
30 | | [lib]/libc.a/lib_a-abort.o | 16 | 0 | 0 |
31 | | [lib]/libc.a/lib_a-closer.o | 36 | 0 | 0 |
32 | | [lib]/libc.a/lib_a-ctype_.o | 257 | 0 | 0 |
33 | | [lib]/libc.a/lib_a-fputwc.o | 264 | 0 | 0 |
34 | ...
35 | ...
36 | ...
37 | | mbed-os/rtos/rtx5/mbed_rtx_handlers.o | 649 | 0 | 0 |
38 | | mbed-os/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/TARGET_FRDM/PeripheralPins.o | 288 | 0 | 0 |
39 | | mbed-os/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/TARGET_MCU_K64F/TARGET_FRDM/fsl_clock_config.o | 120 | 0 | 0 |
40 | | mbed-os/targets/TARGET_Freescale/TARGET_MCUXpresso_MCUS/api/sleep.o | 16 | 0 | 0 |
41 | | Subtotals | 44769 | 2680 | 9080 |
42 | +--------------------------------------------------------------------------------------------------------------------+-------+-------+------+
43 | Total Static RAM memory (data + bss): 11760 bytes
44 | Total Flash memory (text + data): 47449 bytes
45 |
46 | Image: ./BUILD/K64F/GCC_ARM/mbed-os-example-blinky.bin
47 | ```
48 |
49 | To turn this data into the example visualisation:
50 | ```
51 | > mbed compile --stats-depth=100 | ../mbed-os-linker-report/binsize.py -b
52 | ```
53 |
54 | ### Symbol level analysis - arm gcc compiler, modified compiler profile
55 |
56 | For performing the analysis, you need to recompile your program. You need to enable debugging info in your elf file by passing the `-g` option to `gcc`. The default mbed-os compile profile does not do this, hence you need to use the modified profile inside the `compiler_profiles` folder.
57 | ```bash
58 | # For develop profile
59 | > mbed compile -m K64F -t GCC_ARM -c --profile=../mbed-os-linker-report/compiler_profiles/develop.json
60 | # For release profile
61 | > mbed compile -m K64F -t GCC_ARM -c --profile=../mbed-os-linker-report/compiler_profiles/release.json
62 | # For debug profile
63 | > mbed compile -m K64F -t GCC_ARM -c --profile=debug
64 | ```
65 |
66 | Note: if you have a custom compiler profile, you will need to add the `"-g"` flag in the `"common"` section.
67 |
68 | Now run the ELF linker statistics tool:
69 | ```bash
70 | # Process Data: provide one or more elf files for analysis
71 | > python ../mbed-os-linker-report/elfsize.py -i BUILD/K64F/GCC_ARM/mbed-os-example-blinky.elf -b
72 | ```
73 | This will open up a browser page automatically with the visualisation.
74 |
75 | ## Advanced usage
76 | ### More Options
77 | ```
78 | > python binsize.py -h
79 | usage: binsize.py [-h] [-o OUTPUT] [-b]
80 |
81 | Analyse mbed compile output from stdin and generate a json data file for
82 | visualisation
83 |
84 | optional arguments:
85 | -h, --help show this help message and exit
86 | -o OUTPUT, --output OUTPUT
87 | path of output json, defaults to
88 | /Users/leozhou/projects/mbed-os-linker-report/html
89 | /data-flare.js, default filename to data-flare.js if a
90 | folder is specified
91 | -b, --browser launch the pie chart visualisation in a browser
92 |
93 | > python elfsize.py -h
94 | usage: elfsize.py [-h] -i [BINARY [BINARY ...]] [-o OUTPUT] [-b]
95 |
96 | Analyse binary built by gcc and generate json containing binary size
97 | information
98 |
99 | optional arguments:
100 | -h, --help show this help message and exit
101 | -i [BINARY [BINARY ...]], --binary [BINARY [BINARY ...]]
102 | path to the binary. You can also specify multiple
103 | binaries: -i
104 | -o OUTPUT, --output OUTPUT
105 | path of output json, defaults to
106 | /Users/leozhou/projects/mbed-os-linker-report/html
107 | /data-flare.js, default filename to data-flare.js if a
108 | folder is specified
109 | -b, --browser launch the pie chart visualisation in a browser
110 | ```
111 |
112 | ### Example for uVisor statistics
113 | For uVisor the statistics of two ELF files need to be combined into a single JSON file. This is how it works:
114 | ```bash
115 | # Download latest version of a uVisor enabled app
116 | > mbed import mbed-os-example-uvisor
117 | # Change into that directory
118 | > cd mbed-os-example-uvisor
119 | # Recompile uVisor - the command below needs to run twice due to a Makefile bug
120 | > make -C mbed-os/features/FEATURE_UVISOR/importer
121 | # Recompile mbed-os app
122 | # For develop profile
123 | > mbed compile -m K64F -t GCC_ARM -c --profile=../mbed-os-linker-report/compiler_profiles/develop.json
124 | # Combine both elf outputs into a singe JSON file
125 | > python ../mbed-os-linker-report/elfsize.py -i mbed-os/features/FEATURE_UVISOR/importer/TARGET_IGNORE/uvisor/platform/kinetis/release/configuration_kinetis_cortex_m4_0x1fff0000.elf BUILD/K64F/GCC_ARM/mbed-os-example-uvisor.elf -b
126 | ```
127 |
128 | ## Example Output
129 | Below you can find an example screenshot of our tool. Please have a look at our [interactive example](https://armmbed.github.io/mbed-os-linker-report/), too.
130 | 
131 |
--------------------------------------------------------------------------------
/html/sequences.js:
--------------------------------------------------------------------------------
1 | // Dimensions of sunburst.
2 | var width = 1000;
3 | var height = 800;
4 | var radius = Math.min(width, height) / 2;
5 |
6 | // Breadcrumb dimensions: width, height, spacing, width of tip/tail.
7 | var b = {
8 | w: 100, h: 30, s: 3, t: 10
9 | };
10 |
11 | // Mapping of step names to colors.
12 | var colors = {
13 | ".bss": "#5687d1",
14 | ".data": "#7b615c",
15 | ".uvisor.bss": "#de783b",
16 | ".uvisor.secure": "#6ab975",
17 | ".page_heap": "#a173d1",
18 | ".text": "#bbbbbb",
19 | "mbed": "#e0e0e0"
20 | };
21 |
22 | var x = d3.scale.linear()
23 | .range([0, 2 * Math.PI]);
24 |
25 | var y = d3.scale.sqrt()
26 | .range([0, radius]);
27 |
28 | // Total size of all segments; we set this later, after loading the data.
29 | var totalSize = 0;
30 | var topLevel = {};
31 |
32 | var vis = d3.select("#chart").append("svg:svg")
33 | .attr("width", width)
34 | .attr("height", height)
35 | .append("svg:g")
36 | .attr("id", "container")
37 | .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
38 |
39 | var partition = d3.layout.partition()
40 | .value(function(d) { return d.size; });
41 |
42 | var arc = d3.svg.arc()
43 | .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
44 | .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
45 | .innerRadius(function(d) { return Math.max(0, y(d.y)); })
46 | .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
47 |
48 | (function(error, json) {
49 | if (error) throw error;
50 |
51 | // Basic setup of page elements.
52 | initializeBreadcrumbTrail();
53 | drawLegend();
54 |
55 | // Bounding circle underneath the sunburst, to make it easier to detect
56 | // when the mouse leaves the parent g.
57 | vis.append("svg:circle")
58 | .attr("r", radius)
59 | .style("opacity", 0);
60 |
61 | // For efficiency, filter nodes to keep only those large enough to see.
62 | var nodes = partition.nodes(json)
63 | .filter(function(d) {
64 | return (Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))) > 0.005); // 0.005 radians = 0.29 degrees
65 | });
66 |
67 | var path = vis.data([json]).selectAll("path")
68 | .data(nodes)
69 | .enter().append("svg:path")
70 | .attr("d", arc)
71 | .attr("fill-rule", "evenodd")
72 | .style("fill", function(d) {
73 | return (d.depth>1) ? '#0B4C5F' : colors[d.name];
74 | })
75 | .style("opacity", 1)
76 | .on("mouseover", mouseover)
77 | .on("click", click);
78 |
79 | // Add the mouseleave handler to the bounding circle.
80 | d3.select("#container").on("mouseleave", mouseleave);
81 |
82 | // Get total size of the tree = value of root node from partition.
83 | rootNode = path.node().__data__;
84 | totalSize = rootNode.value;
85 | topLevel= rootNode;
86 |
87 | setPercentage(topLevel.value, topLevel.name);
88 |
89 | updateBreadcrumbs([rootNode], "100%");
90 | })(null, mbed_map);
91 |
92 | function getPercentageString(val, tol) {
93 | var percentage = (100 * val / tol).toPrecision(3);
94 | var percentageString = percentage + "%";
95 | if (percentage < 0.1) {
96 | percentageString = "< 0.1%";
97 | }
98 |
99 | return percentageString;
100 | }
101 |
102 | // Fade all but the current sequence, and show it in the breadcrumb trail.
103 | function mouseover(d) {
104 | setPercentage(d.value, d.name);
105 |
106 | var sequenceArray = getAncestors(d);
107 | var percentageStr = getPercentageString(d.value, totalSize);
108 | updateBreadcrumbs(sequenceArray, percentageStr);
109 |
110 | // Fade all the segments.
111 | d3.selectAll("path")
112 | .style("opacity", 0.3);
113 |
114 | // Then highlight only those that are an ancestor of the current segment.
115 | vis.selectAll("path")
116 | .filter(function(node) {
117 | return (sequenceArray.indexOf(node) >= 0);
118 | })
119 | .style("opacity", 1);
120 | }
121 |
122 | // Restore everything to full opacity when moving off the visualization.
123 | function mouseleave(d) {
124 |
125 | // Hide the breadcrumb trail
126 | d3.select("#trail")
127 | .style("visibility", "hidden");
128 |
129 | // Deactivate all segments during transition.
130 | d3.selectAll("path").on("mouseover", null);
131 |
132 | // Transition each segment to full opacity and then reactivate it.
133 | d3.selectAll("path")
134 | .transition()
135 | .duration(1000)
136 | .style("opacity", 1)
137 | .each("end", function() {
138 | d3.select(this).on("mouseover", mouseover);
139 | });
140 |
141 | setPercentage(topLevel.value, topLevel.name);
142 |
143 | var sequenceArray = getAncestors(topLevel);
144 | var percentageStr = getPercentageString(topLevel.value, totalSize);
145 | updateBreadcrumbs(sequenceArray, percentageStr);
146 | }
147 |
148 | function setPercentage(size, name) {
149 | d3.select("#percentage")
150 | .text(size);
151 |
152 | d3.select("#percentage_desc")
153 | .text(name);
154 |
155 | d3.select("#explanation")
156 | .style("visibility", "");
157 | }
158 |
159 | function getRoot(node) {
160 | var current = node;
161 | while (current.parent) {
162 | current = current.parent;
163 | }
164 | return current;
165 | }
166 |
167 | // Given a node in a partition layout, return an array of all of its ancestor
168 | // nodes, highest first, but excluding the root.
169 | function getAncestors(node) {
170 | var path = [];
171 | var current = node;
172 | while (current.parent) {
173 | path.unshift(current);
174 | current = current.parent;
175 | }
176 | path.unshift(current);
177 | return path;
178 | }
179 |
180 | function initializeBreadcrumbTrail() {
181 | // Add the svg area.
182 | var trail = d3.select("#sequence").append("svg:svg")
183 | .attr("width", width*2)
184 | .attr("height", 50)
185 | .attr("id", "trail");
186 | // Add the label at the end, for the percentage.
187 | trail.append("svg:text")
188 | .attr("id", "endlabel")
189 | .style("fill", "#000");
190 | }
191 |
192 | // Generate a string that describes the points of a breadcrumb polygon.
193 | function breadcrumbPoints(d, i) {
194 | var points = [];
195 | points.push("0,0");
196 | points.push(b.w + ",0");
197 | points.push(b.w + b.t + "," + (b.h / 2));
198 | points.push(b.w + "," + b.h);
199 | points.push("0," + b.h);
200 | if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
201 | points.push(b.t + "," + (b.h / 2));
202 | }
203 | return points.join(" ");
204 | }
205 |
206 | // Update the breadcrumb trail to show the current sequence and percentage.
207 | function updateBreadcrumbs(nodeArray, percentageString) {
208 |
209 | // Data join; key function combines name and depth (= position in sequence).
210 | var g = d3.select("#trail")
211 | .selectAll("g")
212 | .data(nodeArray, function(d) { return d.name + d.depth; });
213 |
214 | // Add breadcrumb and label for entering nodes.
215 | var entering = g.enter().append("svg:g");
216 |
217 | entering.append("svg:polygon")
218 | .attr("points", breadcrumbPoints)
219 | .style("fill", function(d) {
220 | return (d.depth>1) ? '#99AA99' : colors[d.name];
221 | });
222 |
223 | entering.append("svg:text")
224 | .attr("x", (b.w + b.t) / 2)
225 | .attr("y", b.h / 2)
226 | .attr("dy", "0.35em")
227 | .attr("text-anchor", "middle")
228 | .text(function(d) { return d.name; });
229 |
230 | // Set position for entering and updating nodes.
231 | g.attr("transform", function(d, i) {
232 | return "translate(" + (i+0.2) * (b.w + b.s) + ", 0)";
233 | });
234 |
235 | // Remove exiting nodes.
236 | g.exit().remove();
237 |
238 | // Now move and update the percentage at the end.
239 | d3.select("#trail").select("#endlabel")
240 | .attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
241 | .attr("y", b.h / 2)
242 | .attr("dy", "0.35em")
243 | .attr("text-anchor", "middle")
244 | .text(percentageString);
245 |
246 | // Make the breadcrumb trail visible, if it's hidden.
247 | d3.select("#trail")
248 | .style("visibility", "");
249 |
250 | }
251 |
252 | function drawLegend() {
253 |
254 | // Dimensions of legend item: width, height, spacing, radius of rounded rect.
255 | var li = {
256 | w: 75, h: 30, s: 3, r: 3
257 | };
258 |
259 | var legend = d3.select("#legend").append("svg:svg")
260 | .attr("width", li.w)
261 | .attr("height", d3.keys(colors).length * (li.h + li.s));
262 |
263 | var g = legend.selectAll("g")
264 | .data(d3.entries(colors))
265 | .enter().append("svg:g")
266 | .attr("transform", function(d, i) {
267 | return "translate(0," + i * (li.h + li.s) + ")";
268 | });
269 |
270 | g.append("svg:rect")
271 | .attr("rx", li.r)
272 | .attr("ry", li.r)
273 | .attr("width", li.w)
274 | .attr("height", li.h)
275 | .style("fill", function(d) { return d.value; });
276 |
277 | g.append("svg:text")
278 | .attr("x", li.w / 2)
279 | .attr("y", li.h / 2)
280 | .attr("dy", "0.35em")
281 | .attr("text-anchor", "middle")
282 | .text(function(d) { return d.key; });
283 | }
284 |
285 | function click(d) {
286 | vis.transition()
287 | .duration(750)
288 | .tween("scale", function() {
289 | var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
290 | yd = d3.interpolate(y.domain(), [d.y, 1]),
291 | yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
292 | return function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); };
293 | })
294 | .selectAll("path")
295 | .attrTween("d", function(d) { return function() { return arc(d); }; });
296 |
297 | topLevel = d;
298 | setPercentage(topLevel.name, topLevel.value);
299 | }
300 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------