summaryrefslogtreecommitdiffstats
path: root/ppapi/generate_ppapi_include_tests.py
blob: 01711badb2cbfd146f9d15b5f56b6cae810d0e1c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/python

# Copyright (c) 2010 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.

# This script should be run manually on occasion to make sure the gyp file and
# the includes tests are up to date.
# It does the following:
# - Verifies that all source code is in ppapi.gyp
# - Verifies that all sources in ppapi.gyp really do exist
# - Generates tests/test_c_includes.c
# - Generates tests/test_cpp_includes.cc
# These tests are checked in to SVN.
# TODO(dmichael):  Make this script execute as a gyp action, move the include
#                  tests to some 'generated' area, and remove them from version
#                  control.

import re
import os
import sys
import posixpath

# A simple regular expression that should match source files for C++ and C.
SOURCE_FILE_RE = re.compile('.+\.(cc|c|h)$')

# IGNORE_RE is a regular expression that matches directories which contain
# source that we don't (currently) expect to be in ppapi.gyp.  This script will
# not check whether source files under these directories are in the gyp file.
# TODO(dmichael): Put examples back in the build.
# TODO(brettw): Put proxy in the build when it's ready.
IGNORE_RE = re.compile('^(examples|GLES2|proxy|tests\/clang).*')

GYP_TARGETS_KEY = 'targets'
GYP_SOURCES_KEY = 'sources'
GYP_TARGET_NAME_KEY = 'target_name'


# Return a set containing all source files found given an object read from a gyp
# file.
def GetAllGypSources(gyp_file_data):
  sources = set([])
  for target in gyp_file_data[GYP_TARGETS_KEY]:
    # Get a list of sources in the target that are not ignored, and 'normalize'
    # them.  The main reason for this is to turn the forward slashes in the gyp
    # file in to backslashes when the script is run on Windows.
    source_list = [posixpath.normpath(src) for src in target[GYP_SOURCES_KEY]
                   if not IGNORE_RE.match(src)]
    sources |= set(source_list)
  return sources


# Search the directory named start_root and all its subdirectories for source
# files.
# Return a set containing the string names of all the source files found,
# relative to start_root.
def GetFileSources(start_root):
  file_set = set([])
  for root, dirs, files in os.walk(start_root):
    relative_root = os.path.relpath(root, start_root)
    if not IGNORE_RE.match(relative_root):
      for source in files:
        if SOURCE_FILE_RE.match(source):
          file_set |= set([os.path.join(relative_root, source)])
  return file_set


# Make sure all source files are in the given gyp object (evaluated from a gyp
# file), and that all source files listed in the gyp object exist in the
# directory.
def VerifyGypFile(gyp_file_data):
  gyp_sources = GetAllGypSources(gyp_file_data)
  file_sources = GetFileSources('.')
  in_gyp_not_file = gyp_sources - file_sources
  in_file_not_gyp = file_sources - gyp_sources
  if len(in_gyp_not_file):
    print 'Found source file(s) in ppapi.gyp but not in the directory:', \
      in_gyp_not_file
  if len(in_file_not_gyp):
    print 'Found source file(s) in the directory but not in ppapi.gyp:', \
      in_file_not_gyp
  error_count = len(in_gyp_not_file) + len(in_file_not_gyp)
  if error_count:
    sys.exit(error_count)


def WriteLines(filename, lines):
  outfile = open(filename, 'w')
  for line in lines:
    outfile.write(line)
  outfile.write('\n')


COPYRIGHT_STRING_C = \
"""/* Copyright (c) 2010 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.
 *
 * This test simply includes all the C headers to ensure they compile with a C
 * compiler.  If it compiles, it passes.
 */
"""

COPYRIGHT_STRING_CC = \
"""// Copyright (c) 2010 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.
//
// This test simply includes all the C++ headers to ensure they compile with a
// C++ compiler.  If it compiles, it passes.
//
"""


# Get the source file names out of the given gyp file data object (as evaluated
# from a gyp file) for the given target name.  Return the string names in
# sorted order.
def GetSourcesForTarget(target_name, gyp_file_data):
  for target in gyp_file_data[GYP_TARGETS_KEY]:
    if target[GYP_TARGET_NAME_KEY] == target_name:
      sources = target[GYP_SOURCES_KEY]
      sources.sort()
      return sources
  print 'Warning: no target named ', target, ' found.'
  return []


# Generate all_c_includes.h, which includes all C headers.  This is part of
# tests/test_c_sizes.c, which includes all C API files to ensure that all
# the headers in ppapi/c can be compiled with a C compiler, and also asserts
# (with compile-time assertions) that all structs and enums are a particular
# size.
def GenerateCIncludeTest(gyp_file_data):
  c_sources = GetSourcesForTarget('ppapi_c', gyp_file_data)
  lines = [COPYRIGHT_STRING_C]
  lines.append('#ifndef PPAPI_TESTS_ALL_C_INCLUDES_H_\n')
  lines.append('#define PPAPI_TESTS_ALL_C_INCLUDES_H_\n\n')
  for source in c_sources:
    lines.append('#include "ppapi/' + source + '"\n')
  lines.append('\n#endif  /* PPAPI_TESTS_ALL_C_INCLUDES_H_ */\n')
  WriteLines('tests/all_c_includes.h', lines)


# Generate all_cpp_includes.h, which is used by test_cpp_includes.cc to ensure
# that all the headers in ppapi/cpp can be compiled with a C++ compiler.
def GenerateCCIncludeTest(gyp_file_data):
  cc_sources = GetSourcesForTarget('ppapi_cpp_objects', gyp_file_data)
  header_re = re.compile('.+\.h$')
  lines = [COPYRIGHT_STRING_CC]
  lines.append('#ifndef PPAPI_TESTS_ALL_CPP_INCLUDES_H_\n')
  lines.append('#define PPAPI_TESTS_ALL_CPP_INCLUDES_H_\n\n')
  for source in cc_sources:
    if header_re.match(source):
      lines.append('#include "ppapi/' + source + '"\n')
  lines.append('\n#endif  // PPAPI_TESTS_ALL_CPP_INCLUDES_H_\n')
  WriteLines('tests/all_cpp_includes.h', lines)


def main():
  ppapi_gyp_file_name = 'ppapi.gyp'
  gyp_file_contents = open(ppapi_gyp_file_name).read()
  gyp_file_data = eval(gyp_file_contents)
  VerifyGypFile(gyp_file_data)
  GenerateCIncludeTest(gyp_file_data)
  GenerateCCIncludeTest(gyp_file_data)
  return 0


if __name__ == '__main__':
    sys.exit(main())