OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2013 The Chromium Authors. All rights reserved. | 3 # Copyright 2013 The Chromium Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 import logging | 7 import logging |
8 import optparse | 8 import optparse |
9 import os | 9 import os |
10 import sys | 10 import sys |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 help='The build CONFIGURATION_NAME.') | 49 help='The build CONFIGURATION_NAME.') |
50 parser.add_option('--proguard-enabled', | 50 parser.add_option('--proguard-enabled', |
51 help='"true" if proguard is enabled.') | 51 help='"true" if proguard is enabled.') |
52 parser.add_option('--proguard-enabled-input-path', | 52 parser.add_option('--proguard-enabled-input-path', |
53 help=('Path to dex in Release mode when proguard ' | 53 help=('Path to dex in Release mode when proguard ' |
54 'is enabled.')) | 54 'is enabled.')) |
55 parser.add_option('--no-locals', | 55 parser.add_option('--no-locals', |
56 help='Exclude locals list from the dex file.') | 56 help='Exclude locals list from the dex file.') |
57 parser.add_option('--multi-dex', default=False, action='store_true', | 57 parser.add_option('--multi-dex', default=False, action='store_true', |
58 help='Create multiple dex files.') | 58 help='Create multiple dex files.') |
| 59 parser.add_option('--incremental', |
| 60 action='store_true', |
| 61 help='Enable incremental builds when possible.') |
59 parser.add_option('--inputs', help='A list of additional input paths.') | 62 parser.add_option('--inputs', help='A list of additional input paths.') |
60 parser.add_option('--excluded-paths', | 63 parser.add_option('--excluded-paths', |
61 help='A list of paths to exclude from the dex file.') | 64 help='A list of paths to exclude from the dex file.') |
62 parser.add_option('--main-dex-list-paths', | 65 parser.add_option('--main-dex-list-paths', |
63 help='A list of paths containing a list of the classes to ' | 66 help='A list of paths containing a list of the classes to ' |
64 'include in the main dex.') | 67 'include in the main dex.') |
65 | 68 |
66 options, paths = parser.parse_args(args) | 69 options, paths = parser.parse_args(args) |
67 | 70 |
68 required_options = ('android_sdk_tools',) | 71 required_options = ('android_sdk_tools',) |
69 build_utils.CheckOptions(options, parser, required=required_options) | 72 build_utils.CheckOptions(options, parser, required=required_options) |
70 | 73 |
71 if options.multi_dex and not options.main_dex_list_paths: | 74 if options.multi_dex and not options.main_dex_list_paths: |
72 logging.warning('--multi-dex is unused without --main-dex-list-paths') | 75 logging.warning('--multi-dex is unused without --main-dex-list-paths') |
73 options.multi_dex = False | 76 options.multi_dex = False |
74 elif options.main_dex_list_paths and not options.multi_dex: | 77 elif options.main_dex_list_paths and not options.multi_dex: |
75 logging.warning('--main-dex-list-paths is unused without --multi-dex') | 78 logging.warning('--main-dex-list-paths is unused without --multi-dex') |
76 | 79 |
77 if options.main_dex_list_paths: | 80 if options.main_dex_list_paths: |
78 options.main_dex_list_paths = build_utils.ParseGypList( | 81 options.main_dex_list_paths = build_utils.ParseGypList( |
79 options.main_dex_list_paths) | 82 options.main_dex_list_paths) |
80 if options.inputs: | 83 if options.inputs: |
81 options.inputs = build_utils.ParseGypList(options.inputs) | 84 options.inputs = build_utils.ParseGypList(options.inputs) |
82 if options.excluded_paths: | 85 if options.excluded_paths: |
83 options.excluded_paths = build_utils.ParseGypList(options.excluded_paths) | 86 options.excluded_paths = build_utils.ParseGypList(options.excluded_paths) |
84 | 87 |
85 return options, paths | 88 return options, paths |
86 | 89 |
87 | 90 |
88 def _OnStaleMd5(options, dex_cmd, paths): | 91 def _AllSubpathsAreClassFiles(paths, changes): |
89 if options.multi_dex: | 92 for path in paths: |
90 combined_main_dex_list = tempfile.NamedTemporaryFile(suffix='.txt') | 93 if any(not p.endswith('.class') for p in changes.IterChangedSubpaths(path)): |
91 combined_main_dex_list.write( | 94 return False |
92 _CreateCombinedMainDexList(options.main_dex_list_paths)) | 95 return True |
93 combined_main_dex_list.flush() | |
94 dex_cmd.append('--main-dex-list=%s' % combined_main_dex_list.name) | |
95 | 96 |
96 dex_cmd += paths | |
97 | 97 |
98 build_utils.CheckOutput(dex_cmd, print_stderr=False) | 98 def _RunDx(changes, options, dex_cmd, paths): |
| 99 with build_utils.TempDir() as classes_temp_dir: |
| 100 # --multi-dex is incompatible with --incremental. |
| 101 if options.multi_dex: |
| 102 combined_main_dex_list = tempfile.NamedTemporaryFile(suffix='.txt') |
| 103 combined_main_dex_list.write( |
| 104 _CreateCombinedMainDexList(options.main_dex_list_paths)) |
| 105 combined_main_dex_list.flush() |
| 106 dex_cmd.append('--main-dex-list=%s' % combined_main_dex_list.name) |
| 107 else: |
| 108 # Use --incremental when .class files are added or modified (never when |
| 109 # removed). |
| 110 # --incremental tells dx to merge all newly dex'ed .class files with |
| 111 # what that already exist in the output dex file (existing classes are |
| 112 # replaced). |
| 113 if options.incremental and changes.AddedOrModifiedOnly(): |
| 114 changed_inputs = set(changes.IterChangedPaths()) |
| 115 changed_paths = [p for p in paths if p in changed_inputs] |
| 116 if not changed_paths: |
| 117 return |
| 118 # When merging in other dex files, there's no easy way to know if |
| 119 # classes were removed from them. |
| 120 if _AllSubpathsAreClassFiles(changed_paths, changes): |
| 121 dex_cmd.append('--incremental') |
| 122 for path in changed_paths: |
| 123 changed_subpaths = set(changes.IterChangedSubpaths(path)) |
| 124 # Not a fundamental restriction, but it's the case right now and it |
| 125 # simplifies the logic to assume so. |
| 126 assert changed_subpaths, 'All inputs should be zip files.' |
| 127 build_utils.ExtractAll(path, path=classes_temp_dir, |
| 128 predicate=lambda p: p in changed_subpaths) |
| 129 paths = [classes_temp_dir] |
| 130 |
| 131 dex_cmd += paths |
| 132 build_utils.CheckOutput(dex_cmd, print_stderr=False) |
99 | 133 |
100 if options.dex_path.endswith('.zip'): | 134 if options.dex_path.endswith('.zip'): |
101 _RemoveUnwantedFilesFromZip(options.dex_path) | 135 _RemoveUnwantedFilesFromZip(options.dex_path) |
102 | 136 |
| 137 |
| 138 def _OnStaleMd5(changes, options, dex_cmd, paths): |
| 139 _RunDx(changes, options, dex_cmd, paths) |
103 build_utils.WriteJson( | 140 build_utils.WriteJson( |
104 [os.path.relpath(p, options.output_directory) for p in paths], | 141 [os.path.relpath(p, options.output_directory) for p in paths], |
105 options.dex_path + '.inputs') | 142 options.dex_path + '.inputs') |
106 | 143 |
107 | 144 |
108 def main(args): | 145 def main(args): |
109 options, paths = _ParseArgs(args) | 146 options, paths = _ParseArgs(args) |
110 if (options.proguard_enabled == 'true' | 147 if (options.proguard_enabled == 'true' |
111 and options.configuration_name == 'Release'): | 148 and options.configuration_name == 'Release'): |
112 paths = [options.proguard_enabled_input_path] | 149 paths = [options.proguard_enabled_input_path] |
(...skipping 23 matching lines...) Expand all Loading... |
136 dex_cmd += [ | 173 dex_cmd += [ |
137 '--multi-dex', | 174 '--multi-dex', |
138 '--minimal-main-dex', | 175 '--minimal-main-dex', |
139 ] | 176 ] |
140 | 177 |
141 output_paths = [ | 178 output_paths = [ |
142 options.dex_path, | 179 options.dex_path, |
143 options.dex_path + '.inputs', | 180 options.dex_path + '.inputs', |
144 ] | 181 ] |
145 | 182 |
| 183 # An escape hatch to be able to check if incremental dexing is causing |
| 184 # problems. |
| 185 force = int(os.environ.get('DISABLE_INCREMENTAL_DX', 0)) |
| 186 |
146 build_utils.CallAndWriteDepfileIfStale( | 187 build_utils.CallAndWriteDepfileIfStale( |
147 lambda: _OnStaleMd5(options, dex_cmd, paths), | 188 lambda changes: _OnStaleMd5(changes, options, dex_cmd, paths), |
148 options, | 189 options, |
149 input_paths=input_paths, | 190 input_paths=input_paths, |
150 input_strings=dex_cmd, | 191 input_strings=dex_cmd, |
151 output_paths=output_paths) | 192 output_paths=output_paths, |
| 193 force=force, |
| 194 pass_changes=True) |
152 | 195 |
153 | 196 |
154 if __name__ == '__main__': | 197 if __name__ == '__main__': |
155 sys.exit(main(sys.argv[1:])) | 198 sys.exit(main(sys.argv[1:])) |
OLD | NEW |