"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/node-gyp/gyp/pylib/gyp/xcode_ninja.py" (11 Apr 2017, 10585 Bytes) of package /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

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