OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 | 2 |
3 # Copyright 2014 Google Inc. | 3 # Copyright 2014 Google Inc. |
4 # | 4 # |
5 # Use of this source code is governed by a BSD-style license that can be | 5 # Use of this source code is governed by a BSD-style license that can be |
6 # found in the LICENSE file. | 6 # found in the LICENSE file. |
7 | 7 |
8 """ | 8 """Functions for parsing the gypd output from gyp. |
9 Functions for parsing the gypd output from gyp. | |
10 """ | 9 """ |
11 | 10 |
12 def parse_dictionary(var_dict, d, current_target_name): | 11 |
13 """ | 12 import os |
14 Helper function to get the meaningful entries in a dictionary. | 13 |
15 @param var_dict VarsDict object for storing the results of the parsing. | 14 |
16 @param d Dictionary object to parse. | 15 def parse_dictionary(var_dict, d, current_target_name, dest_dir): |
17 @param current_target_name The current target being parsed. If this | 16 """Helper function to get the meaningful entries in a dictionary. |
18 dictionary is a target, this will be its entry | 17 |
19 'target_name'. Otherwise, this will be the name of | 18 Parse dictionary d, and store unique relevant entries in var_dict. |
20 the target which contains this dictionary. | 19 Recursively parses internal dictionaries and files that are referenced. |
| 20 When parsing the 'libraries' list from gyp, entries in the form |
| 21 '-l<name>' get assigned to var_dict.LOCAL_SHARED_LIBRARIES as 'lib<name>', |
| 22 and entries in the form '[lib]<name>.a' get assigned to |
| 23 var_dict.LOCAL_STATIC_LIBRARIES as 'lib<name>'. |
| 24 |
| 25 Args: |
| 26 var_dict: VarsDict object for storing the results of the parsing. |
| 27 d: Dictionary object to parse. |
| 28 current_target_name: The current target being parsed. If this dictionary |
| 29 is a target, this will be its entry 'target_name'. Otherwise, this will |
| 30 be the name of the target which contains this dictionary. |
| 31 dest_dir: Destination for the eventual Android.mk that will be created from |
| 32 this parse, relative to Skia trunk. Used to determine path for source |
| 33 files. |
21 """ | 34 """ |
22 for source in d.get('sources', []): | 35 for source in d.get('sources', []): |
23 # Compare against a lowercase version, in case files are named .H or .GYPI | 36 # Compare against a lowercase version, in case files are named .H or .GYPI |
24 lowercase_source = source.lower() | 37 lowercase_source = source.lower() |
25 if lowercase_source.endswith('.h'): | 38 if lowercase_source.endswith('.h'): |
26 # Android.mk does not need the header files. | 39 # Android.mk does not need the header files. |
27 continue | 40 continue |
28 if lowercase_source.endswith('gypi'): | 41 if lowercase_source.endswith('gypi'): |
29 # The gypi files are included in sources, but the sources they included | 42 # The gypi files are included in sources, but the sources they included |
30 # are also included. No need to parse them again. | 43 # are also included. No need to parse them again. |
31 continue | 44 continue |
32 # The path is relative to the gyp folder, but Android wants the path | 45 # The path is relative to the gyp folder, but Android wants the path |
33 # relative to the root. | 46 # relative to dest_dir. |
34 source = source.replace('../src', 'src', 1) | 47 rel_source = os.path.relpath(source, os.pardir) |
35 var_dict.LOCAL_SRC_FILES.add(source) | 48 rel_source = os.path.relpath(rel_source, dest_dir) |
| 49 var_dict.LOCAL_SRC_FILES.add(rel_source) |
36 | 50 |
37 for lib in d.get('libraries', []): | 51 for lib in d.get('libraries', []): |
38 if lib.endswith('.a'): | 52 if lib.endswith('.a'): |
39 # Remove the '.a' | 53 # Remove the '.a' |
40 lib = lib[:-2] | 54 lib = lib[:-2] |
41 # Add 'lib', if necessary | 55 # Add 'lib', if necessary |
42 if not lib.startswith('lib'): | 56 if not lib.startswith('lib'): |
43 lib = 'lib' + lib | 57 lib = 'lib' + lib |
44 var_dict.LOCAL_STATIC_LIBRARIES.add(lib) | 58 var_dict.LOCAL_STATIC_LIBRARIES.add(lib) |
45 else: | 59 else: |
46 # lib will be in the form of '-l<name>'. Change it to 'lib<name>' | 60 # lib will be in the form of '-l<name>'. Change it to 'lib<name>' |
47 lib = lib.replace('-l', 'lib', 1) | 61 lib = lib.replace('-l', 'lib', 1) |
48 var_dict.LOCAL_SHARED_LIBRARIES.add(lib) | 62 var_dict.LOCAL_SHARED_LIBRARIES.add(lib) |
49 | 63 |
50 for dependency in d.get('dependencies', []): | 64 for dependency in d.get('dependencies', []): |
51 # Each dependency is listed as | 65 # Each dependency is listed as |
52 # <path_to_file>:<target>#target | 66 # <path_to_file>:<target>#target |
53 li = dependency.split(':') | 67 li = dependency.split(':') |
54 assert(len(li) <= 2 and len(li) >= 1) | 68 assert(len(li) <= 2 and len(li) >= 1) |
55 sub_targets = [] | 69 sub_targets = [] |
56 if len(li) == 2 and li[1] != '*': | 70 if len(li) == 2 and li[1] != '*': |
57 sub_targets.append(li[1].split('#')[0]) | 71 sub_targets.append(li[1].split('#')[0]) |
58 sub_path = li[0] | 72 sub_path = li[0] |
59 assert(sub_path.endswith('.gyp')) | 73 assert(sub_path.endswith('.gyp')) |
60 # Although the original reference is to a .gyp, parse the corresponding | 74 # Although the original reference is to a .gyp, parse the corresponding |
61 # gypd file, which was constructed by gyp. | 75 # gypd file, which was constructed by gyp. |
62 sub_path = sub_path + 'd' | 76 sub_path = sub_path + 'd' |
63 parse_gypd(var_dict, sub_path, sub_targets) | 77 parse_gypd(var_dict, sub_path, dest_dir, sub_targets) |
64 | 78 |
65 if 'default_configuration' in d: | 79 if 'default_configuration' in d: |
66 config_name = d['default_configuration'] | 80 config_name = d['default_configuration'] |
67 # default_configuration is meaningless without configurations | 81 # default_configuration is meaningless without configurations |
68 assert('configurations' in d) | 82 assert('configurations' in d) |
69 config = d['configurations'][config_name] | 83 config = d['configurations'][config_name] |
70 parse_dictionary(var_dict, config, current_target_name) | 84 parse_dictionary(var_dict, config, current_target_name, dest_dir) |
71 | 85 |
72 for flag in d.get('cflags', []): | 86 for flag in d.get('cflags', []): |
73 var_dict.LOCAL_CFLAGS.add(flag) | 87 var_dict.LOCAL_CFLAGS.add(flag) |
74 for flag in d.get('cflags_cc', []): | 88 for flag in d.get('cflags_cc', []): |
75 var_dict.LOCAL_CPPFLAGS.add(flag) | 89 var_dict.LOCAL_CPPFLAGS.add(flag) |
76 | 90 |
77 for include in d.get('include_dirs', []): | 91 for include in d.get('include_dirs', []): |
78 # The input path will be relative to gyp/, but Android wants relative to | 92 if include.startswith('external'): |
79 # LOCAL_PATH | 93 # This path is relative to the Android root. Leave it alone. |
80 include = include.replace('..', '$(LOCAL_PATH)', 1) | 94 rel_include = include |
| 95 else: |
| 96 # As with source, the input path will be relative to gyp/, but Android |
| 97 # wants relative to dest_dir. |
| 98 rel_include = os.path.relpath(include, os.pardir) |
| 99 rel_include = os.path.relpath(rel_include, dest_dir) |
| 100 rel_include = os.path.join('$(LOCAL_PATH)', rel_include) |
| 101 |
81 # Remove a trailing slash, if present. | 102 # Remove a trailing slash, if present. |
82 if include.endswith('/'): | 103 if rel_include.endswith('/'): |
83 include = include[:-1] | 104 rel_include = rel_include[:-1] |
84 var_dict.LOCAL_C_INCLUDES.add(include) | 105 var_dict.LOCAL_C_INCLUDES.add(rel_include) |
85 # For the top level, libskia, include directories should be exported. | 106 # For the top level, libskia, include directories should be exported. |
| 107 # FIXME (scroggo): Do not hard code this. |
86 if current_target_name == 'libskia': | 108 if current_target_name == 'libskia': |
87 var_dict.LOCAL_EXPORT_C_INCLUDE_DIRS.add(include) | 109 var_dict.LOCAL_EXPORT_C_INCLUDE_DIRS.add(rel_include) |
88 | 110 |
89 for define in d.get('defines', []): | 111 for define in d.get('defines', []): |
90 var_dict.DEFINES.add(define) | 112 var_dict.DEFINES.add(define) |
91 | 113 |
92 | 114 |
93 def parse_gypd(var_dict, path, desired_targets=None): | 115 def parse_gypd(var_dict, path, dest_dir, desired_targets=None): |
94 """ | 116 """Parse a gypd file. |
95 Parse a gypd file. | 117 |
96 @param var_dict VarsDict object for storing the result of the parse. | 118 Open a file that consists of python dictionaries representing build targets. |
97 @param path Path to gypd file. | 119 Parse those dictionaries using parse_dictionary. Recursively parses |
98 @param desired_targets List of targets to be parsed from this file. If empty, | 120 referenced files. |
99 parse all targets. | 121 |
| 122 Args: |
| 123 var_dict: VarsDict object for storing the result of the parse. |
| 124 path: Path to gypd file. |
| 125 dest_dir: Destination for the eventual Android.mk that will be created from |
| 126 this parse, relative to Skia trunk. Used to determine path for source |
| 127 files and include directories. |
| 128 desired_targets: List of targets to be parsed from this file. If empty, |
| 129 parse all targets. |
100 """ | 130 """ |
101 d = {} | 131 d = {} |
102 with open(path, 'r') as f: | 132 with open(path, 'r') as f: |
103 # Read the entire file as a dictionary | 133 # Read the entire file as a dictionary |
104 d = eval(f.read()) | 134 d = eval(f.read()) |
105 | 135 |
106 # The gypd file is structured such that the top level dictionary has an entry | 136 # The gypd file is structured such that the top level dictionary has an entry |
107 # named 'targets' | 137 # named 'targets' |
108 for target in d['targets']: | 138 for target in d['targets']: |
109 target_name = target['target_name'] | 139 target_name = target['target_name'] |
110 if target_name in var_dict.KNOWN_TARGETS: | 140 if target_name in var_dict.KNOWN_TARGETS: |
111 # Avoid circular dependencies | 141 # Avoid circular dependencies |
112 continue | 142 continue |
113 if desired_targets and target_name not in desired_targets: | 143 if desired_targets and target_name not in desired_targets: |
114 # Our caller does not depend on this one | 144 # Our caller does not depend on this one |
115 continue | 145 continue |
116 # Add it to our known targets so we don't parse it again | 146 # Add it to our known targets so we don't parse it again |
117 var_dict.KNOWN_TARGETS.add(target_name) | 147 var_dict.KNOWN_TARGETS.add(target_name) |
118 | 148 |
119 parse_dictionary(var_dict, target, target_name) | 149 parse_dictionary(var_dict, target, target_name, dest_dir) |
120 | 150 |
OLD | NEW |