| #!/usr/bin/python2.4 |
| # Copyright 2009, Google Inc. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following disclaimer |
| # in the documentation and/or other materials provided with the |
| # distribution. |
| # * Neither the name of Google Inc. nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| """Replicate tool for SCons.""" |
| |
| |
| import re |
| |
| |
| def Replicate(env, target, source, **kw): |
| """Replicates (copies) source files/directories to the target directory. |
| |
| Much like env.Install(), with the following differences: |
| * If the source is a directory, recurses through it and calls |
| env.Install() on each source file, rather than copying the entire |
| directory at once. This provides more opportunity for hard linking, and |
| also makes the destination files/directories all writable. |
| * Can take sources which contain env.Glob()-style wildcards. |
| * Can take multiple target directories; will copy to all of them. |
| * Handles duplicate requests. |
| |
| Args: |
| env: Environment in which to operate. |
| target: Destination(s) for copy. Must evaluate to a directory via |
| env.Dir(), or a list of directories. If more than one directory is |
| passed, the entire source list will be copied to each target |
| directory. |
| source: Source file(s) to copy. May be a string, Node, or a list of |
| mixed strings or Nodes. Strings will be passed through env.Glob() to |
| evaluate wildcards. If a source evaluates to a directory, the entire |
| directory will be recursively copied. |
| |
| From env: |
| REPLICATE_REPLACE: A list of pairs of regex search and replacement strings. |
| Each full destination path has substitution performed on each pair |
| (search_regex, replacement) in order. |
| |
| env.Replicate('destdir', ['footxt.txt'], REPLICATE_REPLACE = [ |
| ('\\.txt', '.bar'), ('est', 'ist')]) |
| will copy to 'distdir/footxt.bar' |
| |
| In the example above, note the use of \\ to escape the '.' character, |
| so that it doesn't act like the regexp '.' and match any character. |
| |
| Returns: |
| A list of the destination nodes from the calls to env.Install(). |
| """ |
| replace_list = kw.get('REPLICATE_REPLACE', env.get('REPLICATE_REPLACE', [])) |
| |
| dest_nodes = [] |
| for target_entry in env.Flatten(target): |
| for source_entry in env.Flatten(source): |
| if type(source_entry) == str: |
| # Search for matches for each source entry |
| source_nodes = env.Glob(source_entry) |
| else: |
| # Source entry is already a file or directory node; no need to glob it |
| source_nodes = [source_entry] |
| for s in source_nodes: |
| target_name = env.Dir(target_entry).abspath + '/' + s.name |
| # We need to use the following incantation rather than s.isdir() in |
| # order to handle chained replicates (A -> B -> C). The isdir() |
| # function is not properly defined in all of the Node type classes in |
| # SCons. This change is particularly crucial if hardlinks are present, |
| # in which case using isdir() can cause files to be unintentionally |
| # deleted. |
| # TODO: Look into fixing the innards of SCons so this isn't needed. |
| if str(s.__class__) == 'SCons.Node.FS.Dir': |
| # Recursively copy all files in subdir. Since glob('*') doesn't |
| # match dot files, also glob('.*'). |
| dest_nodes += env.Replicate( |
| target_name, [s.abspath + '/*', s.abspath + '/.*'], |
| REPLICATE_REPLACE=replace_list) |
| else: |
| # Apply replacement strings, if any |
| for r in replace_list: |
| target_name = re.sub(r[0], r[1], target_name) |
| target = env.File(target_name) |
| if (target.has_builder() |
| and hasattr(target.get_builder(), 'name') |
| and target.get_builder().name == 'InstallBuilder' |
| and target.sources == [s]): |
| # Already installed that file, so pass through the destination node |
| # TODO: Is there a better way to determine if this is a duplicate |
| # call to install? |
| dest_nodes += [target] |
| else: |
| dest_nodes += env.InstallAs(target_name, s) |
| |
| # Return list of destination nodes |
| return dest_nodes |
| |
| |
| def generate(env): |
| # NOTE: SCons requires the use of this name, which fails gpylint. |
| """SCons entry point for this tool.""" |
| |
| env.AddMethod(Replicate) |