Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Side by Side Diff: tools/gypi_to_gn.py

Issue 2472813002: [gn] Consolidate exec_script calls to speed up generation (Closed)
Patch Set: fix paths to sources in generate_patched_sdk Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Converts a given gypi file to a python scope and writes the result to stdout. 6 """Converts given gypi files to a python scope and writes the result to stdout.
7 7
8 It is assumed that the file contains a toplevel dictionary, and this script 8 It is assumed that the files contain a toplevel dictionary, and this script
9 will return that dictionary as a GN "scope" (see example below). This script 9 will return that dictionary as a GN "scope" (see example below). This script
10 does not know anything about GYP and it will not expand variables or execute 10 does not know anything about GYP and it will not expand variables or execute
11 conditions. 11 conditions.
12 12
13 It will strip conditions blocks. 13 It will strip conditions blocks.
14 14
15 A variables block at the top level will be flattened so that the variables 15 A variables block at the top level will be flattened so that the variables
16 appear in the root dictionary. This way they can be returned to the GN code. 16 appear in the root dictionary. This way they can be returned to the GN code.
17 17
18 Say your_file.gypi looked like this: 18 Say your_file.gypi looked like this:
19 { 19 {
20 'sources': [ 'a.cc', 'b.cc' ], 20 'sources': [ 'a.cc', 'b.cc' ],
21 'defines': [ 'ENABLE_DOOM_MELON' ], 21 'defines': [ 'ENABLE_DOOM_MELON' ],
22 } 22 }
23 23
24 You would call it like this: 24 You would call it like this:
25 gypi_files = [ "your_file.gypi", "your_other_file.gypi" ]
25 gypi_values = exec_script("//build/gypi_to_gn.py", 26 gypi_values = exec_script("//build/gypi_to_gn.py",
26 [ rebase_path("your_file.gypi") ], 27 [ rebase_path(gypi_files) ],
27 "scope", 28 "scope",
28 [ "your_file.gypi" ]) 29 [ gypi_files ])
29 30
30 Notes: 31 Notes:
31 - The rebase_path call converts the gypi file from being relative to the 32 - The rebase_path call converts the gypi file from being relative to the
32 current build file to being system absolute for calling the script, which 33 current build file to being system absolute for calling the script, which
33 will have a different current directory than this file. 34 will have a different current directory than this file.
34 35
35 - The "scope" parameter tells GN to interpret the result as a series of GN 36 - The "scope" parameter tells GN to interpret the result as a series of GN
36 variable assignments. 37 variable assignments.
37 38
38 - The last file argument to exec_script tells GN that the given file is a 39 - The last file argument to exec_script tells GN that the given file is a
39 dependency of the build so Ninja can automatically re-run GN if the file 40 dependency of the build so Ninja can automatically re-run GN if the file
40 changes. 41 changes.
41 42
42 Read the values into a target like this: 43 Read the values into a target like this:
43 component("mycomponent") { 44 component("mycomponent") {
44 sources = gypi_values.sources 45 sources = gypi_values.your_file_sources
45 defines = gypi_values.defines 46 defines = gypi_values.your_file_defines
46 } 47 }
47 48
48 Sometimes your .gypi file will include paths relative to a different 49 Sometimes your .gypi file will include paths relative to a different
49 directory than the current .gn file. In this case, you can rebase them to 50 directory than the current .gn file. In this case, you can rebase them to
50 be relative to the current directory. 51 be relative to the current directory.
51 sources = rebase_path(gypi_values.sources, ".", 52 sources = rebase_path(gypi_values.your_files_sources, ".",
52 "//path/gypi/input/values/are/relative/to") 53 "//path/gypi/input/values/are/relative/to")
53 54
54 This script will tolerate a 'variables' in the toplevel dictionary or not. If 55 This script will tolerate a 'variables' in the toplevel dictionary or not. If
55 the toplevel dictionary just contains one item called 'variables', it will be 56 the toplevel dictionary just contains one item called 'variables', it will be
56 collapsed away and the result will be the contents of that dictinoary. Some 57 collapsed away and the result will be the contents of that dictinoary. Some
57 .gypi files are written with or without this, depending on how they expect to 58 .gypi files are written with or without this, depending on how they expect to
58 be embedded into a .gyp file. 59 be embedded into a .gyp file.
59 60
60 This script also has the ability to replace certain substrings in the input. 61 This script also has the ability to replace certain substrings in the input.
61 Generally this is used to emulate GYP variable expansion. If you passed the 62 Generally this is used to emulate GYP variable expansion. If you passed the
62 argument "--replace=<(foo)=bar" then all instances of "<(foo)" in strings in 63 argument "--replace=<(foo)=bar" then all instances of "<(foo)" in strings in
63 the input will be replaced with "bar": 64 the input will be replaced with "bar":
64 65
65 gypi_values = exec_script("//build/gypi_to_gn.py", 66 gypi_values = exec_script("//build/gypi_to_gn.py",
66 [ rebase_path("your_file.gypi"), 67 [ rebase_path("your_file.gypi"),
67 "--replace=<(foo)=bar"], 68 "--replace=<(foo)=bar"],
68 "scope", 69 "scope",
69 [ "your_file.gypi" ]) 70 [ "your_file.gypi" ])
70 71
71 """ 72 """
72 73
73 import gn_helpers 74 import gn_helpers
74 from optparse import OptionParser 75 from optparse import OptionParser
75 import sys 76 import sys
77 import os.path
76 78
77 def LoadPythonDictionary(path): 79 def LoadPythonDictionary(path):
78 file_string = open(path).read() 80 file_string = open(path).read()
79 try: 81 try:
80 file_data = eval(file_string, {'__builtins__': None}, None) 82 file_data = eval(file_string, {'__builtins__': None}, None)
81 except SyntaxError, e: 83 except SyntaxError, e:
82 e.filename = path 84 e.filename = path
83 raise 85 raise
84 except Exception, e: 86 except Exception, e:
85 raise Exception("Unexpected error while reading %s: %s" % (path, str(e))) 87 raise Exception("Unexpected error while reading %s: %s" % (path, str(e)))
(...skipping 12 matching lines...) Expand all
98 del file_data['target_conditions'] 100 del file_data['target_conditions']
99 101
100 # Strip targets in the toplevel, since some files define these and we can't 102 # Strip targets in the toplevel, since some files define these and we can't
101 # slurp them in. 103 # slurp them in.
102 if 'targets' in file_data: 104 if 'targets' in file_data:
103 del file_data['targets'] 105 del file_data['targets']
104 106
105 return file_data 107 return file_data
106 108
107 109
108 def ReplaceSubstrings(values, search_for, replace_with):
109 """Recursively replaces substrings in a value.
110
111 Replaces all substrings of the "search_for" with "repace_with" for all
112 strings occurring in "values". This is done by recursively iterating into
113 lists as well as the keys and values of dictionaries."""
114 if isinstance(values, str):
115 return values.replace(search_for, replace_with)
116
117 if isinstance(values, list):
118 return [ReplaceSubstrings(v, search_for, replace_with) for v in values]
119
120 if isinstance(values, dict):
121 # For dictionaries, do the search for both the key and values.
122 result = {}
123 for key, value in values.items():
124 new_key = ReplaceSubstrings(key, search_for, replace_with)
125 new_value = ReplaceSubstrings(value, search_for, replace_with)
126 result[new_key] = new_value
127 return result
128
129 # Assume everything else is unchanged.
130 return values
131
132 def KeepOnly(values, filters): 110 def KeepOnly(values, filters):
133 """Recursively filters out strings not ending in "f" from "values""" 111 """Recursively filters out strings not ending in "f" from "values"""
134 112
135 if isinstance(values, list): 113 if isinstance(values, list):
136 return [v for v in values if v.endswith(tuple(filters))] 114 return [v for v in values if v.endswith(tuple(filters))]
137 115
138 if isinstance(values, dict): 116 if isinstance(values, dict):
139 result = {} 117 result = {}
140 for key, value in values.items(): 118 for key, value in values.items():
141 new_key = KeepOnly(key, filters) 119 new_key = KeepOnly(key, filters)
142 new_value = KeepOnly(value, filters) 120 new_value = KeepOnly(value, filters)
143 result[new_key] = new_value 121 result[new_key] = new_value
144 return result 122 return result
145 123
146 return values 124 return values
147 125
148 def main(): 126 def main():
149 parser = OptionParser() 127 parser = OptionParser()
150 parser.add_option("-r", "--replace", action="append",
151 help="Replaces substrings. If passed a=b, replaces all substrs a with b.")
152 parser.add_option("-k", "--keep_only", default = [], action="append", 128 parser.add_option("-k", "--keep_only", default = [], action="append",
153 help="Keeps only files ending with the listed strings.") 129 help="Keeps only files ending with the listed strings.")
130 parser.add_option("--prefix", action="store_true",
131 help="Prefix variables with base name")
154 (options, args) = parser.parse_args() 132 (options, args) = parser.parse_args()
155 133
156 if len(args) != 1: 134 if len(args) < 1:
157 raise Exception("Need one argument which is the .gypi file to read.") 135 raise Exception("Need at least one .gypi file to read.")
158 136
159 data = LoadPythonDictionary(args[0]) 137 data = {}
160 if options.replace:
161 # Do replacements for all specified patterns.
162 for replace in options.replace:
163 split = replace.split('=')
164 # Allow "foo=" to replace with nothing.
165 if len(split) == 1:
166 split.append('')
167 assert len(split) == 2, "Replacement must be of the form 'key=value'."
168 data = ReplaceSubstrings(data, split[0], split[1])
169 138
170 if options.keep_only != []: 139 for gypi in args:
171 data = KeepOnly(data, options.keep_only) 140 gypi_data = LoadPythonDictionary(gypi)
172 141
173 # Sometimes .gypi files use the GYP syntax with percents at the end of the 142 if options.keep_only != []:
174 # variable name (to indicate not to overwrite a previously-defined value): 143 gypi_data = KeepOnly(gypi_data, options.keep_only)
175 # 'foo%': 'bar', 144
176 # Convert these to regular variables. 145 # Sometimes .gypi files use the GYP syntax with percents at the end of the
177 for key in data: 146 # variable name (to indicate not to overwrite a previously-defined value):
178 if len(key) > 1 and key[len(key) - 1] == '%': 147 # 'foo%': 'bar',
179 data[key[:-1]] = data[key] 148 # Convert these to regular variables.
180 del data[key] 149 for key in gypi_data:
150 if len(key) > 1 and key[len(key) - 1] == '%':
151 gypi_data[key[:-1]] = gypi_data[key]
152 del gypi_data[key]
153 gypi_name = os.path.basename(gypi)[:-len(".gypi")]
154 for key in gypi_data:
155 if options.prefix:
156 # Prefix all variables from this gypi file with the name to disambiguate
157 data[gypi_name + "_" + key] = gypi_data[key]
158 elif key in data:
159 for entry in gypi_data[key]:
160 data[key].append(entry)
161 else:
162 data[key] = gypi_data[key]
181 163
182 print gn_helpers.ToGNString(data) 164 print gn_helpers.ToGNString(data)
183 165
184 if __name__ == '__main__': 166 if __name__ == '__main__':
185 try: 167 try:
186 main() 168 main()
187 except Exception, e: 169 except Exception, e:
188 print str(e) 170 print str(e)
189 sys.exit(1) 171 sys.exit(1)
OLDNEW
« runtime/vm/BUILD.gn ('K') | « runtime/vm/gypi_contents.gni ('k') | tools/process_gypis.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698