• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
2#
3# Protocol Buffers - Google's data interchange format
4# Copyright 2023 Google LLC.  All rights reserved.
5# https://developers.google.com/protocol-buffers/
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions are
9# met:
10#
11#     * Redistributions of source code must retain the above copyright
12# notice, this list of conditions and the following disclaimer.
13#     * Redistributions in binary form must reproduce the above
14# copyright notice, this list of conditions and the following disclaimer
15# in the documentation and/or other materials provided with the
16# distribution.
17#     * Neither the name of Google LLC nor the names of its
18# contributors may be used to endorse or promote products derived from
19# this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33import sys
34import re
35import os
36
37INCLUDE_RE = re.compile('^#include "([^"]*)"')
38
39def parse_include(line):
40  match = INCLUDE_RE.match(line)
41  return match.groups()[0] if match else None
42
43class Amalgamator:
44  def __init__(self, h_out, c_out):
45    self.include_paths = ["."]
46    self.included = set()
47    self.output_h = open(h_out, "w")
48    self.output_c = open(c_out, "w")
49    self.h_out = h_out.split("/")[-1]
50
51  def amalgamate(self, h_files, c_files):
52    self.h_files = set(h_files)
53    self.output_c.write("/* Amalgamated source file */\n")
54    self.output_c.write('#include "%s"\n' % (self.h_out))
55    if self.h_out == "ruby-upb.h":
56      self.output_h.write("// Ruby is still using proto3 enum semantics for proto2\n")
57      self.output_h.write("#define UPB_DISABLE_CLOSED_ENUM_CHECKING\n")
58
59    self.output_h.write("/* Amalgamated source file */\n")
60
61    port_def = self._find_include_file("upb/port/def.inc")
62    port_undef = self._find_include_file("upb/port/undef.inc")
63    self._process_file(port_def, self.output_h)
64    self._process_file(port_def, self.output_c)
65
66    for file in c_files:
67      self._process_file(file, self.output_c)
68
69    self._process_file(port_undef, self.output_h)
70    self._process_file(port_undef, self.output_c)
71
72  def _process_file(self, infile_name, outfile):
73    lines = open(infile_name).readlines()
74
75    has_copyright = lines[0].startswith(
76        "// Protocol Buffers - Google's data interchange format"
77    )
78    if has_copyright:
79      while not lines[0].startswith(
80          "// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH"
81          " DAMAGE"
82      ) and not lines[0].startswith(
83          "// https://developers.google.com/open-source/licenses/bsd"
84      ):
85        lines.pop(0)
86      lines.pop(0)
87
88    for line in lines:
89      if not self._process_include(line):
90        outfile.write(line)
91
92  def _find_include_file(self, name):
93    for h_file in self.h_files:
94      if h_file.endswith(name):
95        return h_file
96
97  def _process_include(self, line):
98    include = parse_include(line)
99    if not include:
100      return False
101    if not (include.startswith("upb") or include.startswith("google")):
102      return False
103    if include and (include.endswith("port/def.inc") or include.endswith("port/undef.inc")):
104      # Skip, we handle this separately
105      return True
106    if include.endswith("hpp"):
107      # Skip, we don't support the amalgamation from C++.
108      return True
109    if re.search(r"stage\d/", include):
110      return True
111    elif include in self.included:
112      return True
113    else:
114      # Include this upb header inline.
115      h_file = self._find_include_file(include)
116      if h_file:
117        self.h_files.remove(h_file)
118        self.included.add(include)
119        self._process_file(h_file, self.output_h)
120        return True
121      raise RuntimeError("Couldn't find include: " + include + ", h_files=" + repr(self.h_files))
122
123# ---- main ----
124
125c_out = sys.argv[1]
126h_out = sys.argv[2]
127amalgamator = Amalgamator(h_out, c_out)
128c_files = []
129h_files = []
130
131for arg in sys.argv[3:]:
132  arg = arg.strip()
133  if arg.endswith(".h") or arg.endswith(".inc"):
134    h_files.append(arg)
135  else:
136    c_files.append(arg)
137
138amalgamator.amalgamate(h_files, c_files)
139