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