• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2015 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import os
6import re
7from util import build_utils
8
9
10class _ProguardOutputFilter(object):
11  """ProGuard outputs boring stuff to stdout (proguard version, jar path, etc)
12  as well as interesting stuff (notes, warnings, etc). If stdout is entirely
13  boring, this class suppresses the output.
14  """
15
16  IGNORE_RE = re.compile(
17      r'(?:Pro.*version|Note:|Reading|Preparing|.*:.*(?:MANIFEST\.MF|\.empty))')
18
19  def __init__(self):
20    self._last_line_ignored = False
21
22  def __call__(self, output):
23    ret = []
24    for line in output.splitlines(True):
25      if not line.startswith(' '):
26        self._last_line_ignored = bool(self.IGNORE_RE.match(line))
27      elif 'You should check if you need to specify' in line:
28        self._last_line_ignored = True
29
30      if not self._last_line_ignored:
31        ret.append(line)
32    return ''.join(ret)
33
34
35class ProguardCmdBuilder(object):
36  def __init__(self, proguard_jar):
37    assert os.path.exists(proguard_jar)
38    self._proguard_jar_path = proguard_jar
39    self._tested_apk_info_path = None
40    self._tested_apk_info = None
41    self._mapping = None
42    self._libraries = None
43    self._injars = None
44    self._configs = None
45    self._outjar = None
46    self._cmd = None
47    self._verbose = False
48
49  def outjar(self, path):
50    assert self._cmd is None
51    assert self._outjar is None
52    self._outjar = path
53
54  def tested_apk_info(self, tested_apk_info_path):
55    assert self._cmd is None
56    assert self._tested_apk_info is None
57    self._tested_apk_info_path = tested_apk_info_path
58
59  def mapping(self, path):
60    assert self._cmd is None
61    assert self._mapping is None
62    assert os.path.exists(path), path
63    self._mapping = path
64
65  def libraryjars(self, paths):
66    assert self._cmd is None
67    assert self._libraries is None
68    for p in paths:
69      assert os.path.exists(p), p
70    self._libraries = paths
71
72  def injars(self, paths):
73    assert self._cmd is None
74    assert self._injars is None
75    for p in paths:
76      assert os.path.exists(p), p
77    self._injars = paths
78
79  def configs(self, paths):
80    assert self._cmd is None
81    assert self._configs is None
82    for p in paths:
83      assert os.path.exists(p), p
84    self._configs = paths
85
86  def verbose(self, verbose):
87    assert self._cmd is None
88    self._verbose = verbose
89
90  def build(self):
91    if self._cmd:
92      return self._cmd
93    assert self._injars is not None
94    assert self._outjar is not None
95    assert self._configs is not None
96    cmd = [
97      'java', '-jar', self._proguard_jar_path,
98      '-forceprocessing',
99    ]
100    if self._tested_apk_info_path:
101      assert len(self._configs) == 1
102      tested_apk_info = build_utils.ReadJson(self._tested_apk_info_path)
103      self._configs += tested_apk_info['configs']
104      self._injars = [
105          p for p in self._injars if not p in tested_apk_info['inputs']]
106      if not self._libraries:
107        self._libraries = []
108      self._libraries += tested_apk_info['inputs']
109      self._mapping = tested_apk_info['mapping']
110      cmd += [
111        '-dontobfuscate',
112        '-dontoptimize',
113        '-dontshrink',
114        '-dontskipnonpubliclibraryclassmembers',
115      ]
116
117    if self._mapping:
118      cmd += [
119        '-applymapping', self._mapping,
120      ]
121
122    if self._libraries:
123      cmd += [
124        '-libraryjars', ':'.join(self._libraries),
125      ]
126
127    cmd += [
128      '-injars', ':'.join(self._injars)
129    ]
130
131    for config_file in self._configs:
132      cmd += ['-include', config_file]
133
134    # The output jar must be specified after inputs.
135    cmd += [
136      '-outjars', self._outjar,
137      '-dump', self._outjar + '.dump',
138      '-printseeds', self._outjar + '.seeds',
139      '-printusage', self._outjar + '.usage',
140      '-printmapping', self._outjar + '.mapping',
141    ]
142
143    if self._verbose:
144      cmd.append('-verbose')
145
146    self._cmd = cmd
147    return self._cmd
148
149  def GetInputs(self):
150    self.build()
151    inputs = [self._proguard_jar_path] + self._configs + self._injars
152    if self._mapping:
153      inputs.append(self._mapping)
154    if self._libraries:
155      inputs += self._libraries
156    if self._tested_apk_info_path:
157      inputs += [self._tested_apk_info_path]
158    return inputs
159
160
161  def CheckOutput(self):
162    self.build()
163    # Proguard will skip writing these files if they would be empty. Create
164    # empty versions of them all now so that they are updated as the build
165    # expects.
166    open(self._outjar + '.dump', 'w').close()
167    open(self._outjar + '.seeds', 'w').close()
168    open(self._outjar + '.usage', 'w').close()
169    open(self._outjar + '.mapping', 'w').close()
170    # Warning: and Error: are sent to stderr, but messages and Note: are sent
171    # to stdout.
172    stdout_filter = None
173    stderr_filter = None
174    if not self._verbose:
175      stdout_filter = _ProguardOutputFilter()
176      stderr_filter = _ProguardOutputFilter()
177    build_utils.CheckOutput(self._cmd, print_stdout=True,
178                            print_stderr=True,
179                            stdout_filter=stdout_filter,
180                            stderr_filter=stderr_filter)
181
182    this_info = {
183      'inputs': self._injars,
184      'configs': self._configs,
185      'mapping': self._outjar + '.mapping',
186    }
187
188    build_utils.WriteJson(this_info, self._outjar + '.info')
189
190