OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 '''SCons integration for GRIT. |
| 7 ''' |
| 8 |
| 9 # NOTE: DO NOT IMPORT ANY GRIT STUFF HERE - we import lazily so that |
| 10 # grit and its dependencies aren't imported until actually needed. |
| 11 |
| 12 import os |
| 13 import types |
| 14 |
| 15 def _IsDebugEnabled(): |
| 16 return 'GRIT_DEBUG' in os.environ and os.environ['GRIT_DEBUG'] == '1' |
| 17 |
| 18 def _SourceToFile(source): |
| 19 '''Return the path to the source file, given the 'source' argument as provided |
| 20 by SCons to the _Builder or _Emitter functions. |
| 21 ''' |
| 22 # Get the filename of the source. The 'source' parameter can be a string, |
| 23 # a "node", or a list of strings or nodes. |
| 24 if isinstance(source, types.ListType): |
| 25 source = str(source[0]) |
| 26 else: |
| 27 source = str(source) |
| 28 return source |
| 29 |
| 30 |
| 31 def _ParseRcFlags(flags): |
| 32 """Gets a mapping of defines. |
| 33 |
| 34 Args: |
| 35 flags: env['RCFLAGS']; the input defines. |
| 36 |
| 37 Returns: |
| 38 A tuple of (defines, res_file): |
| 39 defines: A mapping of {name: val} |
| 40 res_file: None, or the specified res file for static file dependencies. |
| 41 """ |
| 42 from grit import util |
| 43 |
| 44 defines = {} |
| 45 res_file = None |
| 46 # Get the CPP defines from the environment. |
| 47 res_flag = '--res_file=' |
| 48 for flag in flags: |
| 49 if flag.startswith(res_flag): |
| 50 res_file = flag[len(res_flag):] |
| 51 continue |
| 52 if flag.startswith('/D'): |
| 53 flag = flag[2:] |
| 54 name, val = util.ParseDefine(flag) |
| 55 # Only apply to first instance of a given define |
| 56 if name not in defines: |
| 57 defines[name] = val |
| 58 return (defines, res_file) |
| 59 |
| 60 |
| 61 def _Builder(target, source, env): |
| 62 print _SourceToFile(source) |
| 63 |
| 64 from grit import grit_runner |
| 65 from grit.tool import build |
| 66 options = grit_runner.Options() |
| 67 # This sets options to default values |
| 68 options.ReadOptions([]) |
| 69 options.input = _SourceToFile(source) |
| 70 |
| 71 # TODO(joi) Check if we can get the 'verbose' option from the environment. |
| 72 |
| 73 builder = build.RcBuilder(defines=_ParseRcFlags(env['RCFLAGS'])[0]) |
| 74 |
| 75 # To ensure that our output files match what we promised SCons, we |
| 76 # use the list of targets provided by SCons and update the file paths in |
| 77 # our .grd input file with the targets. |
| 78 builder.scons_targets = [str(t) for t in target] |
| 79 builder.Run(options, []) |
| 80 return None # success |
| 81 |
| 82 |
| 83 def _GetOutputFiles(grd, base_dir): |
| 84 """Processes outputs listed in the grd into rc_headers and rc_alls. |
| 85 |
| 86 Note that anything that's not an rc_header is classified as an rc_all. |
| 87 |
| 88 Args: |
| 89 grd: An open GRD reader. |
| 90 |
| 91 Returns: |
| 92 A tuple of (rc_headers, rc_alls, lang_folders): |
| 93 rc_headers: Outputs marked as rc_header. |
| 94 rc_alls: All other outputs. |
| 95 lang_folders: The output language folders. |
| 96 """ |
| 97 rc_headers = [] |
| 98 rc_alls = [] |
| 99 lang_folders = {} |
| 100 |
| 101 # Explicit output files. |
| 102 for output in grd.GetOutputFiles(): |
| 103 path = os.path.join(base_dir, output.GetFilename()) |
| 104 if (output.GetType() == 'rc_header'): |
| 105 rc_headers.append(path) |
| 106 else: |
| 107 rc_alls.append(path) |
| 108 if _IsDebugEnabled(): |
| 109 print 'GRIT: Added target %s' % path |
| 110 if output.attrs['lang'] != '': |
| 111 lang_folders[output.attrs['lang']] = os.path.dirname(path) |
| 112 |
| 113 return (rc_headers, rc_alls, lang_folders) |
| 114 |
| 115 |
| 116 def _ProcessNodes(grd, base_dir, lang_folders): |
| 117 """Processes the GRD nodes to figure out file dependencies. |
| 118 |
| 119 Args: |
| 120 grd: An open GRD reader. |
| 121 base_dir: The base directory for filenames. |
| 122 lang_folders: THe output language folders. |
| 123 |
| 124 Returns: |
| 125 A tuple of (structure_outputs, translated_files, static_files): |
| 126 structure_outputs: Structures marked as sconsdep. |
| 127 translated_files: Files that are structures or skeletons, and get |
| 128 translated by GRIT. |
| 129 static_files: Files that are includes, and are used directly by res files. |
| 130 """ |
| 131 structure_outputs = [] |
| 132 translated_files = [] |
| 133 static_files = [] |
| 134 |
| 135 # Go through nodes, figuring out resources. Also output certain resources |
| 136 # as build targets, based on the sconsdep flag. |
| 137 for node in grd: |
| 138 if node.SatisfiesOutputCondition(): |
| 139 if node.name == 'structure': |
| 140 translated_files.append(os.path.abspath(node.GetFilePath())) |
| 141 # TODO(joi) Should remove the "if sconsdep is true" thing as it is a |
| 142 # hack - see grit/node/structure.py |
| 143 if node.HasFileForLanguage() and node.attrs['sconsdep'] == 'true': |
| 144 for lang in lang_folders: |
| 145 path = node.FileForLanguage(lang, lang_folders[lang], |
| 146 create_file=False, |
| 147 return_if_not_generated=False) |
| 148 if path: |
| 149 structure_outputs.append(path) |
| 150 if _IsDebugEnabled(): |
| 151 print 'GRIT: Added target %s' % path |
| 152 elif (node.name == 'skeleton' or |
| 153 (node.name == 'file' and node.parent and |
| 154 node.parent.name == 'translations')): |
| 155 translated_files.append(os.path.abspath(node.GetFilePath())) |
| 156 elif node.name == 'include': |
| 157 file = node.FilenameToOpen() |
| 158 # If it's added by file name and the file isn't easy to find, don't make |
| 159 # it a dependency. This could add some build flakiness, but it doesn't |
| 160 # work otherwise. |
| 161 if node.attrs['filenameonly'] != 'true' or os.path.exists(file): |
| 162 static_files.append(os.path.abspath(file)) |
| 163 # If it's output from mk, look in the output directory. |
| 164 elif node.attrs['mkoutput'] == 'true': |
| 165 static_files.append(os.path.join(base_dir, os.path.basename(file))) |
| 166 |
| 167 return (structure_outputs, translated_files, static_files) |
| 168 |
| 169 |
| 170 def _SetDependencies(env, base_dir, res_file, rc_alls, translated_files, |
| 171 static_files): |
| 172 """Sets dependencies in the environment. |
| 173 |
| 174 Args: |
| 175 env: The SCons environment. |
| 176 base_dir: The base directory for filenames. |
| 177 res_file: The res_file specified in the RC flags. |
| 178 rc_alls: All non-rc_header outputs. |
| 179 translated_files: Files that are structures or skeletons, and get |
| 180 translated by GRIT. |
| 181 static_files: Files that are includes, and are used directly by res files. |
| 182 """ |
| 183 if res_file: |
| 184 env.Depends(os.path.join(base_dir, res_file), static_files) |
| 185 else: |
| 186 # Make a best effort dependency setup when no res file is specified. |
| 187 translated_files.extend(static_files) |
| 188 |
| 189 for rc_all in rc_alls: |
| 190 env.Depends(rc_all, translated_files) |
| 191 |
| 192 |
| 193 def _Emitter(target, source, env): |
| 194 """Modifies the list of targets to include all outputs. |
| 195 |
| 196 Note that this also sets up the dependencies, even though it's an emitter |
| 197 rather than a scanner. This is so that the resource header file doesn't show |
| 198 as having dependencies. |
| 199 |
| 200 Args: |
| 201 target: The list of targets to emit for. |
| 202 source: The source or list of sources for the target. |
| 203 env: The SCons environment. |
| 204 |
| 205 Returns: |
| 206 A tuple of (targets, sources). |
| 207 """ |
| 208 from grit import grd_reader |
| 209 from grit import util |
| 210 |
| 211 (defines, res_file) = _ParseRcFlags(env['RCFLAGS']) |
| 212 |
| 213 grd = grd_reader.Parse(_SourceToFile(source), debug=_IsDebugEnabled()) |
| 214 # TODO(jperkins): This is a hack to get an output context set for the reader. |
| 215 # This should really be smarter about the language. |
| 216 grd.SetOutputContext('en', defines) |
| 217 |
| 218 base_dir = util.dirname(str(target[0])) |
| 219 (rc_headers, rc_alls, lang_folders) = _GetOutputFiles(grd, base_dir) |
| 220 (structure_outputs, translated_files, static_files) = _ProcessNodes(grd, |
| 221 base_dir, lang_folders) |
| 222 |
| 223 rc_alls.extend(structure_outputs) |
| 224 _SetDependencies(env, base_dir, res_file, rc_alls, translated_files, |
| 225 static_files) |
| 226 |
| 227 targets = rc_headers |
| 228 targets.extend(rc_alls) |
| 229 |
| 230 # Return target and source lists. |
| 231 return (targets, source) |
| 232 |
| 233 |
| 234 # Function name is mandated by newer versions of SCons. |
| 235 def generate(env): |
| 236 # Importing this module should be possible whenever this function is invoked |
| 237 # since it should only be invoked by SCons. |
| 238 import SCons.Builder |
| 239 import SCons.Action |
| 240 |
| 241 # The varlist parameter tells SCons that GRIT needs to be invoked again |
| 242 # if RCFLAGS has changed since last compilation. |
| 243 build_action = SCons.Action.FunctionAction(_Builder, varlist=['RCFLAGS']) |
| 244 emit_action = SCons.Action.FunctionAction(_Emitter, varlist=['RCFLAGS']) |
| 245 |
| 246 builder = SCons.Builder.Builder(action=build_action, emitter=emit_action, |
| 247 src_suffix='.grd') |
| 248 |
| 249 # Add our builder and scanner to the environment. |
| 250 env.Append(BUILDERS = {'GRIT': builder}) |
| 251 |
| 252 |
| 253 # Function name is mandated by newer versions of SCons. |
| 254 def exists(env): |
| 255 return 1 |
OLD | NEW |