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

Side by Side Diff: tools/gypi_to_gn.py

Issue 2492053002: Revert "[gn] Consolidate exec_script calls to speed up generation" (Closed)
Patch Set: 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
« no previous file with comments | « runtime/vm/gypi_contents.gni ('k') | tools/process_gypis.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 given gypi files to a python scope and writes the result to stdout. 6 """Converts a given gypi file to a python scope and writes the result to stdout.
7 7
8 It is assumed that the files contain a toplevel dictionary, and this script 8 It is assumed that the file contains 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" ]
26 gypi_values = exec_script("//build/gypi_to_gn.py", 25 gypi_values = exec_script("//build/gypi_to_gn.py",
27 [ rebase_path(gypi_files) ], 26 [ rebase_path("your_file.gypi") ],
28 "scope", 27 "scope",
29 [ gypi_files ]) 28 [ "your_file.gypi" ])
30 29
31 Notes: 30 Notes:
32 - The rebase_path call converts the gypi file from being relative to the 31 - The rebase_path call converts the gypi file from being relative to the
33 current build file to being system absolute for calling the script, which 32 current build file to being system absolute for calling the script, which
34 will have a different current directory than this file. 33 will have a different current directory than this file.
35 34
36 - The "scope" parameter tells GN to interpret the result as a series of GN 35 - The "scope" parameter tells GN to interpret the result as a series of GN
37 variable assignments. 36 variable assignments.
38 37
39 - The last file argument to exec_script tells GN that the given file is a 38 - The last file argument to exec_script tells GN that the given file is a
40 dependency of the build so Ninja can automatically re-run GN if the file 39 dependency of the build so Ninja can automatically re-run GN if the file
41 changes. 40 changes.
42 41
43 Read the values into a target like this: 42 Read the values into a target like this:
44 component("mycomponent") { 43 component("mycomponent") {
45 sources = gypi_values.your_file_sources 44 sources = gypi_values.sources
46 defines = gypi_values.your_file_defines 45 defines = gypi_values.defines
47 } 46 }
48 47
49 Sometimes your .gypi file will include paths relative to a different 48 Sometimes your .gypi file will include paths relative to a different
50 directory than the current .gn file. In this case, you can rebase them to 49 directory than the current .gn file. In this case, you can rebase them to
51 be relative to the current directory. 50 be relative to the current directory.
52 sources = rebase_path(gypi_values.your_files_sources, ".", 51 sources = rebase_path(gypi_values.sources, ".",
53 "//path/gypi/input/values/are/relative/to") 52 "//path/gypi/input/values/are/relative/to")
54 53
55 This script will tolerate a 'variables' in the toplevel dictionary or not. If 54 This script will tolerate a 'variables' in the toplevel dictionary or not. If
56 the toplevel dictionary just contains one item called 'variables', it will be 55 the toplevel dictionary just contains one item called 'variables', it will be
57 collapsed away and the result will be the contents of that dictinoary. Some 56 collapsed away and the result will be the contents of that dictinoary. Some
58 .gypi files are written with or without this, depending on how they expect to 57 .gypi files are written with or without this, depending on how they expect to
59 be embedded into a .gyp file. 58 be embedded into a .gyp file.
60 59
61 This script also has the ability to replace certain substrings in the input. 60 This script also has the ability to replace certain substrings in the input.
62 Generally this is used to emulate GYP variable expansion. If you passed the 61 Generally this is used to emulate GYP variable expansion. If you passed the
63 argument "--replace=<(foo)=bar" then all instances of "<(foo)" in strings in 62 argument "--replace=<(foo)=bar" then all instances of "<(foo)" in strings in
64 the input will be replaced with "bar": 63 the input will be replaced with "bar":
65 64
66 gypi_values = exec_script("//build/gypi_to_gn.py", 65 gypi_values = exec_script("//build/gypi_to_gn.py",
67 [ rebase_path("your_file.gypi"), 66 [ rebase_path("your_file.gypi"),
68 "--replace=<(foo)=bar"], 67 "--replace=<(foo)=bar"],
69 "scope", 68 "scope",
70 [ "your_file.gypi" ]) 69 [ "your_file.gypi" ])
71 70
72 """ 71 """
73 72
74 import gn_helpers 73 import gn_helpers
75 from optparse import OptionParser 74 from optparse import OptionParser
76 import sys 75 import sys
77 import os.path
78 76
79 def LoadPythonDictionary(path): 77 def LoadPythonDictionary(path):
80 file_string = open(path).read() 78 file_string = open(path).read()
81 try: 79 try:
82 file_data = eval(file_string, {'__builtins__': None}, None) 80 file_data = eval(file_string, {'__builtins__': None}, None)
83 except SyntaxError, e: 81 except SyntaxError, e:
84 e.filename = path 82 e.filename = path
85 raise 83 raise
86 except Exception, e: 84 except Exception, e:
87 raise Exception("Unexpected error while reading %s: %s" % (path, str(e))) 85 raise Exception("Unexpected error while reading %s: %s" % (path, str(e)))
(...skipping 12 matching lines...) Expand all
100 del file_data['target_conditions'] 98 del file_data['target_conditions']
101 99
102 # Strip targets in the toplevel, since some files define these and we can't 100 # Strip targets in the toplevel, since some files define these and we can't
103 # slurp them in. 101 # slurp them in.
104 if 'targets' in file_data: 102 if 'targets' in file_data:
105 del file_data['targets'] 103 del file_data['targets']
106 104
107 return file_data 105 return file_data
108 106
109 107
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
110 def KeepOnly(values, filters): 132 def KeepOnly(values, filters):
111 """Recursively filters out strings not ending in "f" from "values""" 133 """Recursively filters out strings not ending in "f" from "values"""
112 134
113 if isinstance(values, list): 135 if isinstance(values, list):
114 return [v for v in values if v.endswith(tuple(filters))] 136 return [v for v in values if v.endswith(tuple(filters))]
115 137
116 if isinstance(values, dict): 138 if isinstance(values, dict):
117 result = {} 139 result = {}
118 for key, value in values.items(): 140 for key, value in values.items():
119 new_key = KeepOnly(key, filters) 141 new_key = KeepOnly(key, filters)
120 new_value = KeepOnly(value, filters) 142 new_value = KeepOnly(value, filters)
121 result[new_key] = new_value 143 result[new_key] = new_value
122 return result 144 return result
123 145
124 return values 146 return values
125 147
126 def main(): 148 def main():
127 parser = OptionParser() 149 parser = OptionParser()
150 parser.add_option("-r", "--replace", action="append",
151 help="Replaces substrings. If passed a=b, replaces all substrs a with b.")
128 parser.add_option("-k", "--keep_only", default = [], action="append", 152 parser.add_option("-k", "--keep_only", default = [], action="append",
129 help="Keeps only files ending with the listed strings.") 153 help="Keeps only files ending with the listed strings.")
130 parser.add_option("--prefix", action="store_true",
131 help="Prefix variables with base name")
132 (options, args) = parser.parse_args() 154 (options, args) = parser.parse_args()
133 155
134 if len(args) < 1: 156 if len(args) != 1:
135 raise Exception("Need at least one .gypi file to read.") 157 raise Exception("Need one argument which is the .gypi file to read.")
136 158
137 data = {} 159 data = LoadPythonDictionary(args[0])
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])
138 169
139 for gypi in args: 170 if options.keep_only != []:
140 gypi_data = LoadPythonDictionary(gypi) 171 data = KeepOnly(data, options.keep_only)
141 172
142 if options.keep_only != []: 173 # Sometimes .gypi files use the GYP syntax with percents at the end of the
143 gypi_data = KeepOnly(gypi_data, options.keep_only) 174 # variable name (to indicate not to overwrite a previously-defined value):
144 175 # 'foo%': 'bar',
145 # Sometimes .gypi files use the GYP syntax with percents at the end of the 176 # Convert these to regular variables.
146 # variable name (to indicate not to overwrite a previously-defined value): 177 for key in data:
147 # 'foo%': 'bar', 178 if len(key) > 1 and key[len(key) - 1] == '%':
148 # Convert these to regular variables. 179 data[key[:-1]] = data[key]
149 for key in gypi_data: 180 del data[key]
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]
163 181
164 print gn_helpers.ToGNString(data) 182 print gn_helpers.ToGNString(data)
165 183
166 if __name__ == '__main__': 184 if __name__ == '__main__':
167 try: 185 try:
168 main() 186 main()
169 except Exception, e: 187 except Exception, e:
170 print str(e) 188 print str(e)
171 sys.exit(1) 189 sys.exit(1)
OLDNEW
« no previous file with comments | « 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