source: trip-planner-front/node_modules/node-gyp/gyp/pylib/gyp/xcode_ninja.py@ 6fe77af

Last change on this file since 6fe77af was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 11.9 KB
Line 
1# Copyright (c) 2014 Google Inc. 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
5"""Xcode-ninja wrapper project file generator.
6
7This updates the data structures passed to the Xcode gyp generator to build
8with ninja instead. The Xcode project itself is transformed into a list of
9executable targets, each with a build step to build with ninja, and a target
10with every source and resource file. This appears to sidestep some of the
11major performance headaches experienced using complex projects and large number
12of targets within Xcode.
13"""
14
15import errno
16import gyp.generator.ninja
17import os
18import re
19import xml.sax.saxutils
20
21
22def _WriteWorkspace(main_gyp, sources_gyp, params):
23 """ Create a workspace to wrap main and sources gyp paths. """
24 (build_file_root, build_file_ext) = os.path.splitext(main_gyp)
25 workspace_path = build_file_root + ".xcworkspace"
26 options = params["options"]
27 if options.generator_output:
28 workspace_path = os.path.join(options.generator_output, workspace_path)
29 try:
30 os.makedirs(workspace_path)
31 except OSError as e:
32 if e.errno != errno.EEXIST:
33 raise
34 output_string = (
35 '<?xml version="1.0" encoding="UTF-8"?>\n' + '<Workspace version = "1.0">\n'
36 )
37 for gyp_name in [main_gyp, sources_gyp]:
38 name = os.path.splitext(os.path.basename(gyp_name))[0] + ".xcodeproj"
39 name = xml.sax.saxutils.quoteattr("group:" + name)
40 output_string += " <FileRef location = %s></FileRef>\n" % name
41 output_string += "</Workspace>\n"
42
43 workspace_file = os.path.join(workspace_path, "contents.xcworkspacedata")
44
45 try:
46 with open(workspace_file, "r") as input_file:
47 input_string = input_file.read()
48 if input_string == output_string:
49 return
50 except IOError:
51 # Ignore errors if the file doesn't exist.
52 pass
53
54 with open(workspace_file, "w") as output_file:
55 output_file.write(output_string)
56
57
58def _TargetFromSpec(old_spec, params):
59 """ Create fake target for xcode-ninja wrapper. """
60 # Determine ninja top level build dir (e.g. /path/to/out).
61 ninja_toplevel = None
62 jobs = 0
63 if params:
64 options = params["options"]
65 ninja_toplevel = os.path.join(
66 options.toplevel_dir, gyp.generator.ninja.ComputeOutputDir(params)
67 )
68 jobs = params.get("generator_flags", {}).get("xcode_ninja_jobs", 0)
69
70 target_name = old_spec.get("target_name")
71 product_name = old_spec.get("product_name", target_name)
72 product_extension = old_spec.get("product_extension")
73
74 ninja_target = {}
75 ninja_target["target_name"] = target_name
76 ninja_target["product_name"] = product_name
77 if product_extension:
78 ninja_target["product_extension"] = product_extension
79 ninja_target["toolset"] = old_spec.get("toolset")
80 ninja_target["default_configuration"] = old_spec.get("default_configuration")
81 ninja_target["configurations"] = {}
82
83 # Tell Xcode to look in |ninja_toplevel| for build products.
84 new_xcode_settings = {}
85 if ninja_toplevel:
86 new_xcode_settings["CONFIGURATION_BUILD_DIR"] = (
87 "%s/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)" % ninja_toplevel
88 )
89
90 if "configurations" in old_spec:
91 for config in old_spec["configurations"]:
92 old_xcode_settings = old_spec["configurations"][config].get(
93 "xcode_settings", {}
94 )
95 if "IPHONEOS_DEPLOYMENT_TARGET" in old_xcode_settings:
96 new_xcode_settings["CODE_SIGNING_REQUIRED"] = "NO"
97 new_xcode_settings["IPHONEOS_DEPLOYMENT_TARGET"] = old_xcode_settings[
98 "IPHONEOS_DEPLOYMENT_TARGET"
99 ]
100 for key in ["BUNDLE_LOADER", "TEST_HOST"]:
101 if key in old_xcode_settings:
102 new_xcode_settings[key] = old_xcode_settings[key]
103
104 ninja_target["configurations"][config] = {}
105 ninja_target["configurations"][config][
106 "xcode_settings"
107 ] = new_xcode_settings
108
109 ninja_target["mac_bundle"] = old_spec.get("mac_bundle", 0)
110 ninja_target["mac_xctest_bundle"] = old_spec.get("mac_xctest_bundle", 0)
111 ninja_target["ios_app_extension"] = old_spec.get("ios_app_extension", 0)
112 ninja_target["ios_watchkit_extension"] = old_spec.get("ios_watchkit_extension", 0)
113 ninja_target["ios_watchkit_app"] = old_spec.get("ios_watchkit_app", 0)
114 ninja_target["type"] = old_spec["type"]
115 if ninja_toplevel:
116 ninja_target["actions"] = [
117 {
118 "action_name": "Compile and copy %s via ninja" % target_name,
119 "inputs": [],
120 "outputs": [],
121 "action": [
122 "env",
123 "PATH=%s" % os.environ["PATH"],
124 "ninja",
125 "-C",
126 new_xcode_settings["CONFIGURATION_BUILD_DIR"],
127 target_name,
128 ],
129 "message": "Compile and copy %s via ninja" % target_name,
130 },
131 ]
132 if jobs > 0:
133 ninja_target["actions"][0]["action"].extend(("-j", jobs))
134 return ninja_target
135
136
137def IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
138 """Limit targets for Xcode wrapper.
139
140 Xcode sometimes performs poorly with too many targets, so only include
141 proper executable targets, with filters to customize.
142 Arguments:
143 target_extras: Regular expression to always add, matching any target.
144 executable_target_pattern: Regular expression limiting executable targets.
145 spec: Specifications for target.
146 """
147 target_name = spec.get("target_name")
148 # Always include targets matching target_extras.
149 if target_extras is not None and re.search(target_extras, target_name):
150 return True
151
152 # Otherwise just show executable targets and xc_tests.
153 if int(spec.get("mac_xctest_bundle", 0)) != 0 or (
154 spec.get("type", "") == "executable"
155 and spec.get("product_extension", "") != "bundle"
156 ):
157
158 # If there is a filter and the target does not match, exclude the target.
159 if executable_target_pattern is not None:
160 if not re.search(executable_target_pattern, target_name):
161 return False
162 return True
163 return False
164
165
166def CreateWrapper(target_list, target_dicts, data, params):
167 """Initialize targets for the ninja wrapper.
168
169 This sets up the necessary variables in the targets to generate Xcode projects
170 that use ninja as an external builder.
171 Arguments:
172 target_list: List of target pairs: 'base/base.gyp:base'.
173 target_dicts: Dict of target properties keyed on target pair.
174 data: Dict of flattened build files keyed on gyp path.
175 params: Dict of global options for gyp.
176 """
177 orig_gyp = params["build_files"][0]
178 for gyp_name, gyp_dict in data.items():
179 if gyp_name == orig_gyp:
180 depth = gyp_dict["_DEPTH"]
181
182 # Check for custom main gyp name, otherwise use the default CHROMIUM_GYP_FILE
183 # and prepend .ninja before the .gyp extension.
184 generator_flags = params.get("generator_flags", {})
185 main_gyp = generator_flags.get("xcode_ninja_main_gyp", None)
186 if main_gyp is None:
187 (build_file_root, build_file_ext) = os.path.splitext(orig_gyp)
188 main_gyp = build_file_root + ".ninja" + build_file_ext
189
190 # Create new |target_list|, |target_dicts| and |data| data structures.
191 new_target_list = []
192 new_target_dicts = {}
193 new_data = {}
194
195 # Set base keys needed for |data|.
196 new_data[main_gyp] = {}
197 new_data[main_gyp]["included_files"] = []
198 new_data[main_gyp]["targets"] = []
199 new_data[main_gyp]["xcode_settings"] = data[orig_gyp].get("xcode_settings", {})
200
201 # Normally the xcode-ninja generator includes only valid executable targets.
202 # If |xcode_ninja_executable_target_pattern| is set, that list is reduced to
203 # executable targets that match the pattern. (Default all)
204 executable_target_pattern = generator_flags.get(
205 "xcode_ninja_executable_target_pattern", None
206 )
207
208 # For including other non-executable targets, add the matching target name
209 # to the |xcode_ninja_target_pattern| regular expression. (Default none)
210 target_extras = generator_flags.get("xcode_ninja_target_pattern", None)
211
212 for old_qualified_target in target_list:
213 spec = target_dicts[old_qualified_target]
214 if IsValidTargetForWrapper(target_extras, executable_target_pattern, spec):
215 # Add to new_target_list.
216 target_name = spec.get("target_name")
217 new_target_name = "%s:%s#target" % (main_gyp, target_name)
218 new_target_list.append(new_target_name)
219
220 # Add to new_target_dicts.
221 new_target_dicts[new_target_name] = _TargetFromSpec(spec, params)
222
223 # Add to new_data.
224 for old_target in data[old_qualified_target.split(":")[0]]["targets"]:
225 if old_target["target_name"] == target_name:
226 new_data_target = {}
227 new_data_target["target_name"] = old_target["target_name"]
228 new_data_target["toolset"] = old_target["toolset"]
229 new_data[main_gyp]["targets"].append(new_data_target)
230
231 # Create sources target.
232 sources_target_name = "sources_for_indexing"
233 sources_target = _TargetFromSpec(
234 {
235 "target_name": sources_target_name,
236 "toolset": "target",
237 "default_configuration": "Default",
238 "mac_bundle": "0",
239 "type": "executable",
240 },
241 None,
242 )
243
244 # Tell Xcode to look everywhere for headers.
245 sources_target["configurations"] = {"Default": {"include_dirs": [depth]}}
246
247 # Put excluded files into the sources target so they can be opened in Xcode.
248 skip_excluded_files = not generator_flags.get(
249 "xcode_ninja_list_excluded_files", True
250 )
251
252 sources = []
253 for target, target_dict in target_dicts.items():
254 base = os.path.dirname(target)
255 files = target_dict.get("sources", []) + target_dict.get(
256 "mac_bundle_resources", []
257 )
258
259 if not skip_excluded_files:
260 files.extend(
261 target_dict.get("sources_excluded", [])
262 + target_dict.get("mac_bundle_resources_excluded", [])
263 )
264
265 for action in target_dict.get("actions", []):
266 files.extend(action.get("inputs", []))
267
268 if not skip_excluded_files:
269 files.extend(action.get("inputs_excluded", []))
270
271 # Remove files starting with $. These are mostly intermediate files for the
272 # build system.
273 files = [file for file in files if not file.startswith("$")]
274
275 # Make sources relative to root build file.
276 relative_path = os.path.dirname(main_gyp)
277 sources += [
278 os.path.relpath(os.path.join(base, file), relative_path) for file in files
279 ]
280
281 sources_target["sources"] = sorted(set(sources))
282
283 # Put sources_to_index in it's own gyp.
284 sources_gyp = os.path.join(os.path.dirname(main_gyp), sources_target_name + ".gyp")
285 fully_qualified_target_name = "%s:%s#target" % (sources_gyp, sources_target_name)
286
287 # Add to new_target_list, new_target_dicts and new_data.
288 new_target_list.append(fully_qualified_target_name)
289 new_target_dicts[fully_qualified_target_name] = sources_target
290 new_data_target = {}
291 new_data_target["target_name"] = sources_target["target_name"]
292 new_data_target["_DEPTH"] = depth
293 new_data_target["toolset"] = "target"
294 new_data[sources_gyp] = {}
295 new_data[sources_gyp]["targets"] = []
296 new_data[sources_gyp]["included_files"] = []
297 new_data[sources_gyp]["xcode_settings"] = data[orig_gyp].get("xcode_settings", {})
298 new_data[sources_gyp]["targets"].append(new_data_target)
299
300 # Write workspace to file.
301 _WriteWorkspace(main_gyp, sources_gyp, params)
302 return (new_target_list, new_target_dicts, new_data)
Note: See TracBrowser for help on using the repository browser.