• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 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 argparse
6import os
7import shutil
8import sys
9
10
11def detect_encoding(data, default_encoding='UTF-8'):
12  """Detects the encoding used by |data| from the Byte-Order-Mark if present.
13
14  Args:
15    data: string whose encoding needs to be detected
16    default_encoding: encoding returned if no BOM is found.
17
18  Returns:
19    The encoding determined from the BOM if present or |default_encoding| if
20    no BOM was found.
21  """
22  if data.startswith('\xFE\xFF'):
23    return 'UTF-16BE'
24
25  if data.startswith('\xFF\xFE'):
26    return 'UTF-16LE'
27
28  if data.startswith('\xEF\xBB\xBF'):
29    return 'UTF-8'
30
31  return default_encoding
32
33
34def copy_strings_file(source, dest):
35  """Copies a .strings file from |source| to |dest| and convert it to UTF-16.
36
37  Args:
38    source: string, path to the source file
39    dest: string, path to the destination file
40  """
41  with open(source, 'rb') as source_file:
42    data = source_file.read()
43
44  # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call
45  # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints
46  #     CFPropertyListCreateFromXMLData(): Old-style plist parser: missing
47  #     semicolon in dictionary.
48  # on invalid files. Do the same kind of validation.
49  from CoreFoundation import CFDataCreate, CFPropertyListCreateFromXMLData
50  cfdata = CFDataCreate(None, data, len(data))
51  _, error = CFPropertyListCreateFromXMLData(None, cfdata, 0, None)
52  if error:
53    raise ValueError(error)
54
55  encoding = detect_encoding(data)
56  with open(dest, 'wb') as dest_file:
57    dest_file.write(data.decode(encoding).encode('UTF-16'))
58
59
60def copy_file(source, dest):
61  """Copies a file or directory from |source| to |dest|.
62
63  Args:
64    source: string, path to the source file
65    dest: string, path to the destination file
66  """
67  if os.path.isdir(source):
68    if os.path.exists(dest):
69      shutil.rmtree(dest)
70    # Copy tree.
71    # TODO(thakis): This copies file attributes like mtime, while the
72    # single-file branch below doesn't. This should probably be changed to
73    # be consistent with the single-file branch.
74    shutil.copytree(source, dest)
75    return
76
77  if os.path.exists(dest):
78    os.unlink(dest)
79
80  _, extension = os.path.splitext(source)
81  if extension == '.strings':
82    copy_strings_file(source, dest)
83    return
84
85  shutil.copy(source, dest)
86
87
88def main():
89  parser = argparse.ArgumentParser(
90      description='copy source to destination for the creation of a bundle')
91  parser.add_argument('source', help='path to source file or directory')
92  parser.add_argument('dest', help='path to destination')
93  args = parser.parse_args()
94
95  copy_file(args.source, args.dest)
96
97if __name__ == '__main__':
98  main()
99