Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2009, Google Inc.
      4 # All rights reserved.
      5 #
      6 # Redistribution and use in source and binary forms, with or without
      7 # modification, are permitted provided that the following conditions are
      8 # met:
      9 #
     10 #     * Redistributions of source code must retain the above copyright
     11 # notice, this list of conditions and the following disclaimer.
     12 #     * Redistributions in binary form must reproduce the above
     13 # copyright notice, this list of conditions and the following disclaimer
     14 # in the documentation and/or other materials provided with the
     15 # distribution.
     16 #     * Neither the name of Google Inc. nor the names of its
     17 # contributors may be used to endorse or promote products derived from
     18 # this software without specific prior written permission.
     19 #
     20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31 
     32 """fuse_gtest_files.py v0.1.0
     33 Fuses Google Test source code into a .h file and a .cc file.
     34 
     35 SYNOPSIS
     36        fuse_gtest_files.py [GTEST_ROOT_DIR] OUTPUT_DIR
     37 
     38        Scans GTEST_ROOT_DIR for Google Test source code, and generates
     39        two files: OUTPUT_DIR/gtest/gtest.h and OUTPUT_DIR/gtest/gtest-all.cc.
     40        Then you can build your tests by adding OUTPUT_DIR to the include
     41        search path and linking with OUTPUT_DIR/gtest/gtest-all.cc.  These
     42        two files contain everything you need to use Google Test.  Hence
     43        you can "install" Google Test by copying them to wherever you want.
     44 
     45        GTEST_ROOT_DIR can be omitted and defaults to the parent directory
     46        of the directory holding the fuse_gtest_files.py script.
     47 
     48 EXAMPLES
     49        ./fuse_gtest_files.py fused_gtest
     50        ./fuse_gtest_files.py path/to/unpacked/gtest fused_gtest
     51 
     52 This tool is experimental.  In particular, it assumes that there is no
     53 conditional inclusion of Google Test headers.  Please report any
     54 problems to googletestframework (at] googlegroups.com.  You can read
     55 http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide for
     56 more information.
     57 """
     58 
     59 __author__ = 'wan (at] google.com (Zhanyong Wan)'
     60 
     61 import os
     62 import re
     63 import sets
     64 import sys
     65 
     66 # Regex for matching '#include <gtest/...>'.
     67 INCLUDE_GTEST_FILE_REGEX = re.compile(r'^\s*#\s*include\s*<(gtest/.+)>')
     68 
     69 # Regex for matching '#include "src/..."'.
     70 INCLUDE_SRC_FILE_REGEX = re.compile(r'^\s*#\s*include\s*"(src/.+)"')
     71 
     72 # Where to find the source files.
     73 GTEST_H_SEED = 'include/gtest/gtest.h'
     74 GTEST_SPI_H_SEED = 'include/gtest/gtest-spi.h'
     75 GTEST_ALL_CC_SEED = 'src/gtest-all.cc'
     76 
     77 # Where to put the generated files.
     78 GTEST_H_OUTPUT = 'gtest/gtest.h'
     79 GTEST_ALL_CC_OUTPUT = 'gtest/gtest-all.cc'
     80 
     81 
     82 def GetGTestRootDir():
     83   """Returns the absolute path to the Google Test root directory.
     84 
     85   We assume that this script is in a sub-directory of the Google Test root.
     86   """
     87 
     88   my_path = sys.argv[0]  # Path to this script.
     89   my_dir = os.path.dirname(my_path)
     90   if not my_dir:
     91     my_dir = '.'
     92 
     93   return os.path.abspath(os.path.join(my_dir, '..'))
     94 
     95 
     96 def ValidateGTestRootDir(gtest_root):
     97   """Makes sure gtest_root points to a valid gtest root directory.
     98 
     99   The function aborts the program on failure.
    100   """
    101 
    102   def VerifyFileExists(relative_path):
    103     """Verifies that the given file exists; aborts on failure.
    104 
    105     relative_path is the file path relative to the gtest root.
    106     """
    107 
    108     if not os.path.isfile(os.path.join(gtest_root, relative_path)):
    109       print 'ERROR: Cannot find %s in directory %s.' % (relative_path,
    110                                                         gtest_root)
    111       print ('Please either specify a valid Google Test root directory '
    112              'or omit it on the command line.')
    113       sys.exit(1)
    114 
    115   VerifyFileExists(GTEST_H_SEED)
    116   VerifyFileExists(GTEST_ALL_CC_SEED)
    117 
    118 
    119 def ValidateOutputDir(output_dir):
    120   """Makes sure output_dir points to a valid output directory.
    121 
    122   The function aborts the program on failure.
    123   """
    124 
    125   def VerifyOutputFile(relative_path):
    126     """Verifies that the given output file path is valid.
    127 
    128     relative_path is relative to the output_dir directory.
    129     """
    130 
    131     # Makes sure the output file either doesn't exist or can be overwritten.
    132     output_file = os.path.join(output_dir, relative_path)
    133     if os.path.exists(output_file):
    134       print ('%s already exists in directory %s - overwrite it? (y/N) ' %
    135              (relative_path, output_dir))
    136       answer = sys.stdin.readline().strip()
    137       if answer not in ['y', 'Y']:
    138         print 'ABORTED.'
    139         sys.exit(1)
    140 
    141     # Makes sure the directory holding the output file exists; creates
    142     # it and all its ancestors if necessary.
    143     parent_directory = os.path.dirname(output_file)
    144     if not os.path.isdir(parent_directory):
    145       os.makedirs(parent_directory)
    146 
    147   VerifyOutputFile(GTEST_H_OUTPUT)
    148   VerifyOutputFile(GTEST_ALL_CC_OUTPUT)
    149 
    150 
    151 def FuseGTestH(gtest_root, output_dir):
    152   """Scans folder gtest_root to generate gtest/gtest.h in output_dir."""
    153 
    154   output_file = file(os.path.join(output_dir, GTEST_H_OUTPUT), 'w')
    155   processed_files = sets.Set()  # Holds all gtest headers we've processed.
    156 
    157   def ProcessFile(gtest_header_path):
    158     """Processes the given gtest header file."""
    159 
    160     # We don't process the same header twice.
    161     if gtest_header_path in processed_files:
    162       return
    163 
    164     processed_files.add(gtest_header_path)
    165 
    166     # Reads each line in the given gtest header.
    167     for line in file(os.path.join(gtest_root, gtest_header_path), 'r'):
    168       m = INCLUDE_GTEST_FILE_REGEX.match(line)
    169       if m:
    170         # It's '#include <gtest/...>' - let's process it recursively.
    171         ProcessFile('include/' + m.group(1))
    172       else:
    173         # Otherwise we copy the line unchanged to the output file.
    174         output_file.write(line)
    175 
    176   ProcessFile(GTEST_H_SEED)
    177   output_file.close()
    178 
    179 
    180 def FuseGTestAllCc(gtest_root, output_dir):
    181   """Scans folder gtest_root to generate gtest/gtest-all.cc in output_dir."""
    182 
    183   output_file = file(os.path.join(output_dir, GTEST_ALL_CC_OUTPUT), 'w')
    184   processed_files = sets.Set()
    185 
    186   def ProcessFile(gtest_source_file):
    187     """Processes the given gtest source file."""
    188 
    189     # We don't process the same #included file twice.
    190     if gtest_source_file in processed_files:
    191       return
    192 
    193     processed_files.add(gtest_source_file)
    194 
    195     # Reads each line in the given gtest source file.
    196     for line in file(os.path.join(gtest_root, gtest_source_file), 'r'):
    197       m = INCLUDE_GTEST_FILE_REGEX.match(line)
    198       if m:
    199         if 'include/' + m.group(1) == GTEST_SPI_H_SEED:
    200           # It's '#include <gtest/gtest-spi.h>'.  This file is not
    201           # #included by <gtest/gtest.h>, so we need to process it.
    202           ProcessFile(GTEST_SPI_H_SEED)
    203         else:
    204           # It's '#include <gtest/foo.h>' where foo is not gtest-spi.
    205           # We treat it as '#include <gtest/gtest.h>', as all other
    206           # gtest headers are being fused into gtest.h and cannot be
    207           # #included directly.
    208 
    209           # There is no need to #include <gtest/gtest.h> more than once.
    210           if not GTEST_H_SEED in processed_files:
    211             processed_files.add(GTEST_H_SEED)
    212             output_file.write('#include <%s>\n' % (GTEST_H_OUTPUT,))
    213       else:
    214         m = INCLUDE_SRC_FILE_REGEX.match(line)
    215         if m:
    216           # It's '#include "src/foo"' - let's process it recursively.
    217           ProcessFile(m.group(1))
    218         else:
    219           output_file.write(line)
    220 
    221   ProcessFile(GTEST_ALL_CC_SEED)
    222   output_file.close()
    223 
    224 
    225 def FuseGTest(gtest_root, output_dir):
    226   ValidateGTestRootDir(gtest_root)
    227   ValidateOutputDir(output_dir)
    228 
    229   FuseGTestH(gtest_root, output_dir)
    230   FuseGTestAllCc(gtest_root, output_dir)
    231 
    232 
    233 def main():
    234   argc = len(sys.argv)
    235   if argc == 2:
    236     # fuse_gtest_files.py OUTPUT_DIR
    237     FuseGTest(GetGTestRootDir(), sys.argv[1])
    238   elif argc == 3:
    239     # fuse_gtest_files.py GTEST_ROOT_DIR OUTPUT_DIR
    240     FuseGTest(sys.argv[1], sys.argv[2])
    241   else:
    242     print __doc__
    243     sys.exit(1)
    244 
    245 
    246 if __name__ == '__main__':
    247   main()
    248