Merge pull request #5 from hcindyl:build-antlr4 PiperOrigin-RevId: 526120720 Change-Id: I3c72bd6192a8fa3c91288b46d91f7999d08816cc
diff --git a/gitfiles/_bazelrc b/gitfiles/_bazelrc new file mode 100644 index 0000000..24e7dea --- /dev/null +++ b/gitfiles/_bazelrc
@@ -0,0 +1,17 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Build configurations for the project +build --action_env=BAZEL_CXXOPTS="-std=c++17" +build --action_env=CC="clang"
diff --git a/gitfiles/_gitignore b/gitfiles/_gitignore new file mode 100644 index 0000000..653d61b --- /dev/null +++ b/gitfiles/_gitignore
@@ -0,0 +1,6 @@ +# Ignore all bazel related directories + +bazel-bin +bazel-mpact-sim +bazel-out +bazel-testlogs
diff --git a/gitfiles/antlr_cc.bzl b/gitfiles/antlr_cc.bzl new file mode 100644 index 0000000..2457742 --- /dev/null +++ b/gitfiles/antlr_cc.bzl
@@ -0,0 +1,189 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Build rules to create C++ code from an Antlr4 grammar.""" + +def antlr4_cc_lexer( + name, + src, + namespaces = None, + deps = None): + """Generates the C++ source corresponding to an antlr4 lexer definition. + + Args: + name: The name of the package to use for the cc_library. + src: The antlr4 g4 file containing the lexer rules. + namespaces: The namespace used by the generated files. Uses an array to + support nested namespaces. Defaults to [name]. + deps: Dependencies for the generated code. + """ + namespaces = namespaces or [name] + deps = deps or [] + if not src.endswith(".g4"): + fail("Grammar must end with .g4", "src") + file_prefix = _label_basename(src[:-3]) + base_file_prefix = _strip_end(file_prefix, "Lexer") + out_files = [ + "%sLexer.h" % base_file_prefix, + "%sLexer.cpp" % base_file_prefix, + ] + command = ";\n".join([ + # Use the first namespace, we'll add the others afterwards. + _make_tool_invocation_command(namespaces[0]), + _make_namespace_adjustment_command(namespaces, out_files), + _make_google3_cleanup_command(out_files), + ]) + native.genrule( + name = name + "_source", + srcs = [src], + outs = out_files, + cmd = command, + heuristic_label_expansion = 0, + tools = [Label("@org_antlr_tool//file")], + ) + native.cc_library( + name = name, + srcs = [f for f in out_files if f.endswith(".cpp")], + hdrs = [f for f in out_files if f.endswith(".h")], + deps = [Label("@org_antlr4_cpp_runtime//:antlr4")] + deps, + copts = [ + "-fexceptions", + "-iquote $(BINDIR)/external/org_antlr4_cpp_runtime/antlr4/include/antlr4-runtime", + ], + features = ["-use_header_modules"], # Incompatible with -fexceptions. + ) + +def antlr4_cc_parser( + name, + src, + namespaces = None, + listener = True, + visitor = False, + deps = None): + """Generates the C++ source corresponding to an antlr4 parser definition. + + Args: + name: The name of the package to use for the cc_library. + src: The antlr4 g4 file containing the parser rules. + namespaces: The namespace used by the generated files. Uses an array to + support nested namespaces. Defaults to [name]. + listener: Whether or not to include listener generated files. + visitor: Whether or not to include visitor generated files. + deps: Dependencies for the generated code. + """ + suffixes = () + if listener: + suffixes += ( + "%sBaseListener.cpp", + "%sListener.cpp", + "%sBaseListener.h", + "%sListener.h", + ) + if visitor: + suffixes += ( + "%sBaseVisitor.cpp", + "%sVisitor.cpp", + "%sBaseVisitor.h", + "%sVisitor.h", + ) + namespaces = namespaces or [name] + deps = deps or [] + if not src.endswith(".g4"): + fail("Grammar must end with .g4", "src") + file_prefix = _label_basename(src[:-3]) + base_file_prefix = _strip_end(file_prefix, "Parser") + out_files = [ + "%sParser.h" % base_file_prefix, + "%sParser.cpp" % base_file_prefix, + ] + _make_outs(file_prefix, suffixes) + command = ";\n".join([ + # Use the first namespace, we'll add the others afterward. + _make_tool_invocation_command(namespaces[0], listener, visitor), + _make_namespace_adjustment_command(namespaces, out_files), + _make_google3_cleanup_command(out_files), + ]) + native.genrule( + name = name + "_source", + srcs = [src], + outs = out_files, + cmd = command, + heuristic_label_expansion = 0, + tools = [Label("@org_antlr_tool//file")], + ) + native.cc_library( + name = name, + srcs = [f for f in out_files if f.endswith(".cpp")], + hdrs = [f for f in out_files if f.endswith(".h")], + deps = [Label("@org_antlr4_cpp_runtime//:antlr4")] + deps, + copts = [ + "-fexceptions", + "-Wno-nonnull", + "-iquote $(BINDIR)/external/org_antlr4_cpp_runtime/antlr4/include/antlr4-runtime", + ], + features = ["-use_header_modules"], + ) + +def _make_tool_invocation_command(package, listener = False, visitor = False): + return "java -jar $(location @org_antlr_tool//file) " + \ + "$(SRCS)" + \ + (" -visitor" if visitor else " -no-visitor") + \ + (" -listener" if listener else " -no-listener") + \ + " -Dlanguage=Cpp" + \ + " -package " + package + \ + " -o $(@D)" + \ + " -Xexact-output-dir" + +def _make_google3_cleanup_command(out_files): + # Clean up imports and usage of the #pragma once directive. + return ";\n".join([ + ";\n".join([ + "sed -i '0,/antlr4-runtime.h/s//antlr4-runtime\\/antlr4-runtime.h/' $(@D)/%s" % filepath, + "grep -q '^#pragma once' $(@D)/%s && echo '\n#endif // %s\n' >> $(@D)/%s" % (filepath, _to_c_macro_name(filepath), filepath), + "sed -i '0,/#pragma once/s//#ifndef %s\\n#define %s/' $(@D)/%s" % (_to_c_macro_name(filepath), _to_c_macro_name(filepath), filepath), + ]) + for filepath in out_files + ]) + +def _make_namespace_adjustment_command(namespaces, out_files): + if len(namespaces) == 1: + return "true" + commands = [] + extra_header_namespaces = "\\\n".join(["namespace %s {" % namespace for namespace in namespaces[1:]]) + for filepath in out_files: + if filepath.endswith(".h"): + commands.append("sed -i '/namespace %s {/ a%s' $(@D)/%s" % (namespaces[0], extra_header_namespaces, filepath)) + for namespace in namespaces[1:]: + commands.append("sed -i '/} \\/\\/ namespace %s/i} \\/\\/ namespace %s' $(@D)/%s" % (namespaces[0], namespace, filepath)) + else: + commands.append("sed -i 's/using namespace %s;/using namespace %s;/' $(@D)/%s" % (namespaces[0], "::".join(namespaces), filepath)) + return ";\n".join(commands) + +def _make_outs(file_prefix, suffixes): + return [file_suffix % file_prefix for file_suffix in suffixes] + +def _strip_end(text, suffix): + if not text.endswith(suffix): + return text + return text[:len(text) - len(suffix)] + +def _to_c_macro_name(filename): + # Convert the filenames to a format suitable for C preprocessor definitions. + char_list = [filename[i].upper() for i in range(len(filename))] + return "ANTLR4_GEN_" + "".join( + [a if (("A" <= a) and (a <= "Z")) else "_" for a in char_list], + ) + +def _label_basename(label): + """Returns the basename of a Blaze label.""" + return label.split(":")[-1]