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

Unified Diff: grit/scons.py

Issue 9956102: Add SCons integration for [ grit build ] tool (inputs and outputs). (Closed) Base URL: https://grit-i18n.googlecode.com/svn/trunk
Patch Set: . Created 8 years, 9 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: grit/scons.py
diff --git a/grit/scons.py b/grit/scons.py
new file mode 100755
index 0000000000000000000000000000000000000000..52a27f9ddf413908fc47dcb115f360214f46c9b8
--- /dev/null
+++ b/grit/scons.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+'''SCons integration for GRIT.
+'''
+
+# NOTE: DO NOT IMPORT ANY GRIT STUFF HERE - we import lazily so that
+# grit and its dependencies aren't imported until actually needed.
+
+import os
+import types
+
+def _IsDebugEnabled():
+ return 'GRIT_DEBUG' in os.environ and os.environ['GRIT_DEBUG'] == '1'
+
+def _SourceToFile(source):
+ '''Return the path to the source file, given the 'source' argument as provided
+ by SCons to the _Builder or _Emitter functions.
+ '''
+ # Get the filename of the source. The 'source' parameter can be a string,
+ # a "node", or a list of strings or nodes.
+ if isinstance(source, types.ListType):
+ source = str(source[0])
+ else:
+ source = str(source)
+ return source
+
+
+def _ParseRcFlags(flags):
+ """Gets a mapping of defines.
+
+ Args:
+ flags: env['RCFLAGS']; the input defines.
+
+ Returns:
+ A tuple of (defines, res_file):
+ defines: A mapping of {name: val}
+ res_file: None, or the specified res file for static file dependencies.
+ """
+ from grit import util
+
+ defines = {}
+ res_file = None
+ # Get the CPP defines from the environment.
+ res_flag = '--res_file='
+ for flag in flags:
+ if flag.startswith(res_flag):
+ res_file = flag[len(res_flag):]
+ continue
+ if flag.startswith('/D'):
+ flag = flag[2:]
+ name, val = util.ParseDefine(flag)
+ # Only apply to first instance of a given define
+ if name not in defines:
+ defines[name] = val
+ return (defines, res_file)
+
+
+def _Builder(target, source, env):
+ print _SourceToFile(source)
+
+ from grit import grit_runner
+ from grit.tool import build
+ options = grit_runner.Options()
+ # This sets options to default values
+ options.ReadOptions([])
+ options.input = _SourceToFile(source)
+
+ # TODO(joi) Check if we can get the 'verbose' option from the environment.
+
+ builder = build.RcBuilder(defines=_ParseRcFlags(env['RCFLAGS'])[0])
+
+ # To ensure that our output files match what we promised SCons, we
+ # use the list of targets provided by SCons and update the file paths in
+ # our .grd input file with the targets.
+ builder.scons_targets = [str(t) for t in target]
+ builder.Run(options, [])
+ return None # success
+
+
+def _GetOutputFiles(grd, base_dir):
+ """Processes outputs listed in the grd into rc_headers and rc_alls.
+
+ Note that anything that's not an rc_header is classified as an rc_all.
+
+ Args:
+ grd: An open GRD reader.
+
+ Returns:
+ A tuple of (rc_headers, rc_alls, lang_folders):
+ rc_headers: Outputs marked as rc_header.
+ rc_alls: All other outputs.
+ lang_folders: The output language folders.
+ """
+ rc_headers = []
+ rc_alls = []
+ lang_folders = {}
+
+ # Explicit output files.
+ for output in grd.GetOutputFiles():
+ path = os.path.join(base_dir, output.GetFilename())
+ if (output.GetType() == 'rc_header'):
+ rc_headers.append(path)
+ else:
+ rc_alls.append(path)
+ if _IsDebugEnabled():
+ print 'GRIT: Added target %s' % path
+ if output.attrs['lang'] != '':
+ lang_folders[output.attrs['lang']] = os.path.dirname(path)
+
+ return (rc_headers, rc_alls, lang_folders)
+
+
+def _ProcessNodes(grd, base_dir, lang_folders):
+ """Processes the GRD nodes to figure out file dependencies.
+
+ Args:
+ grd: An open GRD reader.
+ base_dir: The base directory for filenames.
+ lang_folders: THe output language folders.
+
+ Returns:
+ A tuple of (structure_outputs, translated_files, static_files):
+ structure_outputs: Structures marked as sconsdep.
+ translated_files: Files that are structures or skeletons, and get
+ translated by GRIT.
+ static_files: Files that are includes, and are used directly by res files.
+ """
+ structure_outputs = []
+ translated_files = []
+ static_files = []
+
+ # Go through nodes, figuring out resources. Also output certain resources
+ # as build targets, based on the sconsdep flag.
+ for node in grd:
+ if node.SatisfiesOutputCondition():
+ if node.name == 'structure':
+ translated_files.append(os.path.abspath(node.GetFilePath()))
+ # TODO(joi) Should remove the "if sconsdep is true" thing as it is a
+ # hack - see grit/node/structure.py
+ if node.HasFileForLanguage() and node.attrs['sconsdep'] == 'true':
+ for lang in lang_folders:
+ path = node.FileForLanguage(lang, lang_folders[lang],
+ create_file=False,
+ return_if_not_generated=False)
+ if path:
+ structure_outputs.append(path)
+ if _IsDebugEnabled():
+ print 'GRIT: Added target %s' % path
+ elif (node.name == 'skeleton' or
+ (node.name == 'file' and node.parent and
+ node.parent.name == 'translations')):
+ translated_files.append(os.path.abspath(node.GetFilePath()))
+ elif node.name == 'include':
+ file = node.FilenameToOpen()
+ # If it's added by file name and the file isn't easy to find, don't make
+ # it a dependency. This could add some build flakiness, but it doesn't
+ # work otherwise.
+ if node.attrs['filenameonly'] != 'true' or os.path.exists(file):
+ static_files.append(os.path.abspath(file))
+ # If it's output from mk, look in the output directory.
+ elif node.attrs['mkoutput'] == 'true':
+ static_files.append(os.path.join(base_dir, os.path.basename(file)))
+
+ return (structure_outputs, translated_files, static_files)
+
+
+def _SetDependencies(env, base_dir, res_file, rc_alls, translated_files,
+ static_files):
+ """Sets dependencies in the environment.
+
+ Args:
+ env: The SCons environment.
+ base_dir: The base directory for filenames.
+ res_file: The res_file specified in the RC flags.
+ rc_alls: All non-rc_header outputs.
+ translated_files: Files that are structures or skeletons, and get
+ translated by GRIT.
+ static_files: Files that are includes, and are used directly by res files.
+ """
+ if res_file:
+ env.Depends(os.path.join(base_dir, res_file), static_files)
+ else:
+ # Make a best effort dependency setup when no res file is specified.
+ translated_files.extend(static_files)
+
+ for rc_all in rc_alls:
+ env.Depends(rc_all, translated_files)
+
+
+def _Emitter(target, source, env):
+ """Modifies the list of targets to include all outputs.
+
+ Note that this also sets up the dependencies, even though it's an emitter
+ rather than a scanner. This is so that the resource header file doesn't show
+ as having dependencies.
+
+ Args:
+ target: The list of targets to emit for.
+ source: The source or list of sources for the target.
+ env: The SCons environment.
+
+ Returns:
+ A tuple of (targets, sources).
+ """
+ from grit import grd_reader
+ from grit import util
+
+ (defines, res_file) = _ParseRcFlags(env['RCFLAGS'])
+
+ grd = grd_reader.Parse(_SourceToFile(source), debug=_IsDebugEnabled())
+ # TODO(jperkins): This is a hack to get an output context set for the reader.
+ # This should really be smarter about the language.
+ grd.SetOutputContext('en', defines)
+
+ base_dir = util.dirname(str(target[0]))
+ (rc_headers, rc_alls, lang_folders) = _GetOutputFiles(grd, base_dir)
+ (structure_outputs, translated_files, static_files) = _ProcessNodes(grd,
+ base_dir, lang_folders)
+
+ rc_alls.extend(structure_outputs)
+ _SetDependencies(env, base_dir, res_file, rc_alls, translated_files,
+ static_files)
+
+ targets = rc_headers
+ targets.extend(rc_alls)
+
+ # Return target and source lists.
+ return (targets, source)
+
+
+# Function name is mandated by newer versions of SCons.
+def generate(env):
+ # Importing this module should be possible whenever this function is invoked
+ # since it should only be invoked by SCons.
+ import SCons.Builder
+ import SCons.Action
+
+ # The varlist parameter tells SCons that GRIT needs to be invoked again
+ # if RCFLAGS has changed since last compilation.
+ build_action = SCons.Action.FunctionAction(_Builder, varlist=['RCFLAGS'])
+ emit_action = SCons.Action.FunctionAction(_Emitter, varlist=['RCFLAGS'])
+
+ builder = SCons.Builder.Builder(action=build_action, emitter=emit_action,
+ src_suffix='.grd')
+
+ # Add our builder and scanner to the environment.
+ env.Append(BUILDERS = {'GRIT': builder})
+
+
+# Function name is mandated by newer versions of SCons.
+def exists(env):
+ return 1
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698