# SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2025-2025, The OpenROAD Authors """A Python SWIG wrapping rule These rules generate a C++ src file that is expected to be used as srcs in cc_library or cc_binary rules. See below for expected usage. cc_library(srcs=[":python_foo"]) python_wrap_cc(name = "python_foo", srcs=["exception.i"],...) """ load( "//bazel:swig_common.bzl", "get_transitive_includes", "get_transitive_options", "get_transitive_srcs", ) PythonSwigInfo = provider( "PythonSwigInfo for taking dependencies on other swig info rules", fields = [ "transitive_srcs", "includes", "swig_options", ], ) PYTHON_STABLE_API_DEFINE = "Py_LIMITED_API=0x030A0000" PYTHON_EXTENSION_LINKOPTS = select({ "@platforms//os:macos": [ "-undefined", "dynamic_lookup", ], "//conditions:default": [], }) def _python_wrap_cc_impl(ctx): """Generates a single C++ file from the provided srcs in a DefaultInfo.""" if len(ctx.files.srcs) > 1 and not ctx.attr.root_swig_src: fail("If multiple src files are provided, root_swig_src must be specified.") swig_lib_dir = ctx.file._swig_swg.dirname root_file = ctx.file.root_swig_src or ctx.files.srcs[0] cc_outfile_name = ctx.attr.out or (ctx.attr.name + ".cc") cc_output_file = ctx.actions.declare_file(cc_outfile_name) py_outfile_name = ctx.attr.module + ".py" py_output_file = ctx.actions.declare_file(py_outfile_name) include_root_directory = "" if ctx.label.workspace_root: include_root_directory = ctx.label.workspace_root + "/" if ctx.label.package: include_root_directory += ctx.label.package + "/" src_inputs = get_transitive_srcs( PythonSwigInfo, ctx.files.srcs + ctx.files.root_swig_src, ctx.attr.deps, ) includes_paths = get_transitive_includes( PythonSwigInfo, ["{}{}".format(include_root_directory, include) for include in ctx.attr.swig_includes], ctx.attr.deps, ) swig_options = get_transitive_options(PythonSwigInfo, ctx.attr.swig_options, ctx.attr.deps) args = ctx.actions.args() args.add("-DBAZEL=1") args.add("-python") args.add("-c++") args.add("-flatstaticmethod") args.add("-module") args.add(ctx.attr.module) args.add_all(swig_options.to_list()) args.add_all(includes_paths.to_list(), format_each = "-I%s") args.add("-o") args.add(cc_output_file.path) args.add(root_file.path) ctx.actions.run( outputs = [cc_output_file, py_output_file], inputs = src_inputs, arguments = [args], env = {"SWIG_LIB": swig_lib_dir}, tools = ctx.files._swig_lib, executable = ctx.executable._swig, ) return [ DefaultInfo(files = depset([cc_output_file, py_output_file])), PythonSwigInfo( transitive_srcs = src_inputs, includes = includes_paths, swig_options = swig_options, ), ] python_wrap_cc = rule( implementation = _python_wrap_cc_impl, attrs = { "deps": attr.label_list( allow_empty = True, doc = "python_wrap_cc dependencies", providers = [PythonSwigInfo], ), "module": attr.string( mandatory = True, doc = "swig module", ), "out": attr.string( doc = "The name of the C++ source file generated by these rules. If not set, defaults to '.cc'.", ), "root_swig_src": attr.label( allow_single_file = [".swig", ".i"], doc = """If more than one swig file is included in this rule. The root file must be explicitly provided. This is the file which will be passed to swig for generation.""", ), "srcs": attr.label_list( allow_empty = False, allow_files = [".i", ".swig", ".h", ".hpp", ".hh"], doc = "Swig files that generate C++ files", ), "swig_includes": attr.string_list( doc = "List of directories relative to the BUILD file to append as -I flags to SWIG", ), "swig_options": attr.string_list( doc = "args to pass directly to the swig binary", ), "_swig": attr.label( default = "@swig", allow_files = True, cfg = "exec", executable = True, ), "_swig_lib": attr.label( default = "@swig//:lib_python", allow_files = True, ), "_swig_swg": attr.label( default = "@swig//:swig_swg", allow_single_file = True, doc = "SWIG swig.swg library file used for determining SWIG_LIB " + "env variable (internal attribute).", ), }, )