| """GNU Awk — Bazel build driven by rules_cc_autoconf. |
| |
| `config.h` is generated by `autoconf_hdr` from upstream `configh.in`, probing |
| the real `cc_toolchain` instead of shipping per-OS pre-baked headers. Every |
| `AC_ARG_ENABLE` / `AC_ARG_WITH` option from upstream `configure.ac` surfaces |
| as a `bool_flag` or `label_flag` wired to a `config.h` macro via |
| `checks.AC_BUILD_SETTING`. |
| """ |
| |
| load("@bazel_skylib//rules:common_settings.bzl", "bool_flag") |
| load("@bazel_skylib//rules:write_file.bzl", "write_file") |
| load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") |
| load("@rules_cc_autoconf//autoconf:autoconf.bzl", "autoconf") |
| load("@rules_cc_autoconf//autoconf:autoconf_hdr.bzl", "autoconf_hdr") |
| load("@rules_cc_autoconf//autoconf:autoconf_srcs.bzl", "autoconf_srcs") |
| load("@rules_cc_autoconf//autoconf:checks.bzl", "checks", "macros", "utils") |
| load("@rules_cc_autoconf//autoconf:package_info.bzl", "package_info") |
| load(":gawk_minimal.bzl", "gawk_minimal") |
| |
| exports_files([ |
| "gawk_tool.sh.tpl", |
| "MODULE.bazel", |
| ]) |
| |
| # --------------------------------------------------------------------------- |
| # User-facing build flags (mirror gawk's AC_ARG_ENABLE / AC_ARG_WITH options). |
| # --------------------------------------------------------------------------- |
| |
| # Where configure says `--disable-FOO`, we declare a positively-named |
| # `:disable_foo` flag (default False). When True, the matching `#define` is |
| # emitted. This avoids the awkward "True means default behavior" inversion. |
| |
| # Each entry declares a public `bool_flag` (default False) and, when |
| # `paired_setting = True`, a private `_<name>_setting` `config_setting` |
| # the rest of this file `select()`s on. `versioned_extension_dir` has |
| # no companion setting because no rule consumes it yet (declared for |
| # API parity with upstream's `--enable-versioned-extension-dir`). |
| _BOOL_FLAGS = [ |
| ("disable_lint", True), |
| ("builtin_intdiv0", True), |
| ("enable_mpfr", True), |
| ("disable_pma", True), |
| ("disable_extensions", True), |
| ("enable_readline", True), |
| ("versioned_extension_dir", False), |
| ] |
| |
| [ |
| bool_flag( |
| name = name, |
| build_setting_default = False, |
| visibility = ["//visibility:public"], |
| ) |
| for name, _ in _BOOL_FLAGS |
| ] |
| |
| [ |
| config_setting( |
| name = "_{}_setting".format(name), |
| flag_values = {":" + name: "True"}, |
| visibility = ["//:__subpackages__"], |
| ) |
| for name, paired in _BOOL_FLAGS |
| if paired |
| ] |
| |
| # --------------------------------------------------------------------------- |
| # label_flag providers for MPFR / readline. |
| # |
| # We deliberately don't add `bazel_dep`s on `@mpfr` / `@readline` in |
| # MODULE.bazel — the readline BCR module pulls in `@ncurses`, which itself |
| # uses gawk to generate `term.h`, creating an unbreakable analysis-time |
| # cycle. Letting the user supply their own provider via these label_flags |
| # avoids that, and works even when the upstream BCR modules don't link |
| # cleanly in a given environment. |
| # |
| # When the matching `:enable_<lib>` bool_flag is True but the label_flag |
| # is left at its default `:_<lib>_unset` sentinel, the compile fails with |
| # a clear `#error` pointing the user at the right flag to set. |
| # --------------------------------------------------------------------------- |
| |
| # Each entry declares a public `label_flag` whose default is a private |
| # `_<name>_unset` cc_library that emits a `#error` pointing the user at |
| # the right flag to set, so flipping the matching `:enable_<name>` flag |
| # without supplying a real provider fails with a clear message. |
| _LABEL_FLAGS = [ |
| ("mpfr", "enable_mpfr", "@mpfr//:mpfr"), |
| ("readline", "enable_readline", "@readline//:readline"), |
| ] |
| |
| [ |
| write_file( |
| name = "_{}_unset_c".format(name), |
| out = "_{}_unset.c".format(name), |
| content = [ |
| "#error \"`//:{flag}` is True but `//:{name}` was left at its default sentinel. Set `--//:{name}=<your {name} provider>` (e.g. `--//:{name}={example}`) to enable {name} support.\"".format( |
| name = name, |
| example = example, |
| flag = flag, |
| ), |
| ], |
| ) |
| for name, flag, example in _LABEL_FLAGS |
| ] |
| |
| [ |
| cc_library( |
| name = "_{}_unset".format(name), |
| srcs = [":_{}_unset_c".format(name)], |
| tags = ["manual"], |
| ) |
| for name, _, _ in _LABEL_FLAGS |
| ] |
| |
| [ |
| label_flag( |
| name = name, |
| build_setting_default = ":_{}_unset".format(name), |
| tags = ["manual"], |
| visibility = ["//visibility:public"], |
| ) |
| for name, _, _ in _LABEL_FLAGS |
| ] |
| |
| # --------------------------------------------------------------------------- |
| # AC_INIT — package_info. |
| # --------------------------------------------------------------------------- |
| |
| package_info( |
| name = "gawk_package_info", |
| package_name = "GNU Awk", |
| aliases = { |
| "PACKAGE": "PACKAGE_TARNAME", |
| "VERSION": "PACKAGE_VERSION", |
| }, |
| module_bazel = "MODULE.bazel", |
| package_bugreport = "bug-gawk@gnu.org", |
| package_tarname = "gawk", |
| package_url = "https://www.gnu.org/software/gawk/", |
| strip_bcr_version = True, |
| ) |
| |
| # --------------------------------------------------------------------------- |
| # Per-m4 autoconf targets — inlined into `:configure_ac` below. |
| # |
| # Bison's overlay maintains one `autoconf(name = "m4_<stem>")` target per |
| # upstream m4 file so other gnulib m4 modules can `dep` on whatever check |
| # each one produces. For gawk that machinery isn't load-bearing: we've |
| # disabled NLS, libtool, gettext, glibc-specific probes, and z/OS support, |
| # so the corresponding m4 files (arch, ax_check_compile_flag, glibc2, |
| # glibc21, intdiv0, intldir, intl, intmax, inttypes-pri, libtool, |
| # lt~obsolete, ltoptions, ltsugar, ltversion, triplet-transformation, |
| # noreturn) become no-ops, and the remaining ones (longdouble, signed, |
| # uintmax_t, wchar_t, socket, pma, mpfr, readline) only ever emit a |
| # handful of `AC_DEFINE` lines that nothing depends on transitively. |
| # Inlining them avoids 24 dead targets and a top-level dep list. |
| # --------------------------------------------------------------------------- |
| |
| # --------------------------------------------------------------------------- |
| # Top-level configure.ac translation. |
| # --------------------------------------------------------------------------- |
| |
| # AC_CHECK_HEADERS list from configure.ac:196-202 + AC_CHECK_HEADERS_ONCE. |
| # Plus the standard set autoconf includes via AC_USE_SYSTEM_EXTENSIONS / autoheader. |
| GAWK_HEADER_PROBES = [ |
| "arpa/inet.h", |
| "fcntl.h", |
| "inttypes.h", |
| "libintl.h", |
| "locale.h", |
| "mcheck.h", |
| "memory.h", |
| "netdb.h", |
| "netinet/in.h", |
| "spawn.h", |
| "stdbool.h", |
| "stddef.h", |
| "stdint.h", |
| "stdio.h", |
| "stdlib.h", |
| "string.h", |
| "strings.h", |
| "stropts.h", |
| "sys/ioctl.h", |
| "sys/param.h", |
| "sys/personality.h", |
| "sys/procctl.h", |
| "sys/select.h", |
| "sys/socket.h", |
| "sys/stat.h", |
| "sys/sysctl.h", |
| "sys/time.h", |
| "sys/types.h", |
| "sys/wait.h", |
| "termios.h", |
| "uchar.h", |
| "unistd.h", |
| "wchar.h", |
| "wctype.h", |
| ] |
| |
| # AC_CHECK_FUNCS list from configure.ac:318-328. |
| GAWK_FUNCTION_PROBES = [ |
| "__etoa_l", |
| "_NSGetExecutablePath", |
| "atexit", |
| "btowc", |
| "c32rtomb", |
| "clock_gettime", |
| "fmod", |
| "fwrite_unlocked", |
| "gai_strerror", |
| "getaddrinfo", |
| "getdtablesize", |
| "getgrent", |
| "getgroups", |
| "gettimeofday", |
| "grantpt", |
| "isascii", |
| "isblank", |
| "lstat", |
| "mbrtoc32", |
| "mtrace", |
| "personality", |
| "posix_openpt", |
| "posix_spawnp", |
| "procctl", |
| "setenv", |
| "setlocale", |
| "setsid", |
| "sigprocmask", |
| "strcasecmp", |
| "strcoll", |
| "strftime", |
| "strncasecmp", |
| "strsignal", |
| "sysctl", |
| "timegm", |
| "waitpid", |
| "wctype", |
| ] |
| |
| autoconf( |
| name = "configure_ac", |
| checks = ( |
| # --- Standard header probes (~30 HAVE_*_H) --- |
| macros.AC_CHECK_HEADERS(GAWK_HEADER_PROBES) + |
| # --- Standard function probes (~35 HAVE_*) --- |
| macros.AC_CHECK_FUNCS(GAWK_FUNCTION_PROBES) + |
| # --- Struct member probes --- |
| macros.AC_CHECK_MEMBERS( |
| ["struct stat.st_blksize"], |
| includes = [ |
| "#include <sys/stat.h>", |
| "#include <sys/types.h>", |
| ], |
| ) + macros.AC_CHECK_MEMBERS( |
| ["struct passwd.pw_passwd"], |
| includes = [ |
| "#include <pwd.h>", |
| "#include <sys/types.h>", |
| ], |
| ) + macros.AC_CHECK_MEMBERS( |
| ["struct group.gr_passwd"], |
| includes = [ |
| "#include <grp.h>", |
| "#include <sys/types.h>", |
| ], |
| ) + macros.AC_CHECK_MEMBERS( |
| ["struct tm.tm_zone"], |
| includes = ["#include <time.h>"], |
| ) + [ |
| # `HAVE_TM_ZONE` is the deprecated alias for |
| # `HAVE_STRUCT_TM_TM_ZONE` — emit it only when the member probe |
| # above succeeded. |
| checks.AC_DEFINE( |
| "HAVE_TM_ZONE", |
| 1, |
| condition = "ac_cv_member_struct_tm_tm_zone", |
| ), |
| # --- Type probes (replace baked-in assumptions with real checks) --- |
| # Upstream `configh.in` only has slots for these four; gawk |
| # source doesn't reference `HAVE_LONG_DOUBLE` / `HAVE_WCHAR_T`. |
| checks.AC_CHECK_TYPE( |
| "long long int", |
| define = "HAVE_LONG_LONG_INT", |
| ), |
| checks.AC_CHECK_TYPE( |
| "unsigned long long int", |
| define = "HAVE_UNSIGNED_LONG_LONG_INT", |
| ), |
| checks.AC_CHECK_TYPE( |
| "intmax_t", |
| define = "HAVE_INTMAX_T", |
| includes = ["#include <stdint.h>"], |
| ), |
| checks.AC_CHECK_TYPE( |
| "uintmax_t", |
| define = "HAVE_UINTMAX_T", |
| includes = ["#include <stdint.h>"], |
| ), |
| # --- Sizeof probes --- |
| checks.AC_CHECK_SIZEOF( |
| "unsigned int", |
| define = "SIZEOF_UNSIGNED_INT", |
| ), |
| checks.AC_CHECK_SIZEOF( |
| "unsigned long", |
| define = "SIZEOF_UNSIGNED_LONG", |
| ), |
| checks.AC_CHECK_SIZEOF( |
| "void *", |
| define = "SIZEOF_VOID_P", |
| ), |
| # --- Custom compile probes from configure.ac:231-250 --- |
| checks.AC_TRY_COMPILE( |
| name = "gawk_cv_time_t_in_sys_types_h", |
| code = utils.AC_LANG_PROGRAM( |
| ["#include <sys/types.h>"], |
| "time_t foo; foo = 0; (void)foo;", |
| ), |
| define = "TIME_T_IN_SYS_TYPES_H", |
| ), |
| checks.AC_TRY_COMPILE( |
| name = "gawk_cv_have_wctype_t", |
| code = utils.AC_LANG_PROGRAM( |
| ["#include <wctype.h>"], |
| "wctype_t foo; foo = 0; (void)foo;", |
| ), |
| define = "HAVE_WCTYPE_T", |
| ), |
| checks.AC_TRY_COMPILE( |
| name = "gawk_cv_have_wint_t", |
| code = utils.AC_LANG_PROGRAM( |
| ["#include <wctype.h>"], |
| "wint_t foo; foo = 0; (void)foo;", |
| ), |
| define = "HAVE_WINT_T", |
| ), |
| checks.AC_TRY_COMPILE( |
| name = "gawk_cv_have_sockaddr_storage", |
| code = utils.AC_LANG_PROGRAM( |
| [ |
| "#include <sys/socket.h>", |
| "#include <sys/types.h>", |
| ], |
| "struct sockaddr_storage foo; (void)foo;", |
| ), |
| define = "HAVE_SOCKADDR_STORAGE", |
| ), |
| # AC_C_RESTRICT |
| checks.AC_C_RESTRICT(), |
| # AC_FUNC_MBRTOWC (rules_cc_autoconf has no dedicated macro; probe directly) |
| checks.AC_TRY_LINK( |
| name = "gawk_cv_func_mbrtowc", |
| code = utils.AC_LANG_PROGRAM( |
| ["#include <wchar.h>"], |
| "mbstate_t st; mbrtowc(0, \"\", 0, &st);", |
| ), |
| define = "HAVE_MBRTOWC", |
| ), |
| # AC_FUNC_GETPGRP probe — accepts `getpgrp()` with zero args |
| # (the POSIX-2001 form). Old BSD systems took a pid. |
| checks.AC_TRY_COMPILE( |
| name = "ac_cv_func_getpgrp_void", |
| code = utils.AC_LANG_PROGRAM( |
| ["#include <unistd.h>"], |
| "(void)getpgrp();", |
| ), |
| define = "GETPGRP_VOID", |
| ), |
| ] + select({ |
| # AC_TYPE_GETGROUPS: POSIX uses gid_t, Windows doesn't ship |
| # getgroups at all — keeping this as a platform select avoids a |
| # probe that would otherwise need to compile a known-bad signature |
| # to discriminate. |
| "@platforms//os:windows": [checks.AC_DEFINE("GETGROUPS_T", "int")], |
| "//conditions:default": [checks.AC_DEFINE("GETGROUPS_T", "gid_t")], |
| }) + [ |
| # AC_USE_SYSTEM_EXTENSIONS proxy. Upstream sets these |
| # unconditionally on every host (it's gnulib's `m4/extensions.m4`), |
| # not just glibc. `_GNU_SOURCE` in particular is what makes |
| # `support/regex.h` set its own `__USE_GNU` (lines 30-33), which |
| # in turn exposes `struct re_registers` — without it, gawk fails |
| # to compile against its own vendored regex on macOS as well as |
| # any other libc. |
| checks.AC_DEFINE("_GNU_SOURCE", 1), |
| checks.AC_DEFINE("_ALL_SOURCE", 1), |
| checks.AC_DEFINE("__EXTENSIONS__", 1), |
| checks.AC_DEFINE("_POSIX_PTHREAD_SEMANTICS", 1), |
| checks.AC_DEFINE("_TANDEM_SOURCE", 1), |
| ] + select({ |
| # macOS-specific feature toggle on top of the shared set. |
| "@platforms//os:macos": [checks.AC_DEFINE("_DARWIN_C_SOURCE", 1)], |
| "//conditions:default": [], |
| }) + select({ |
| # Runtime probes (AC_RUN_IFELSE for printf %F / %a). Modern glibc and |
| # Apple libc support both formats; assume yes on POSIX. |
| "@platforms//os:windows": [], |
| "//conditions:default": [ |
| checks.AC_DEFINE("PRINTF_HAS_F_FORMAT", 1), |
| checks.AC_DEFINE("PRINTF_HAS_A_FORMAT", 1), |
| ], |
| }) + [ |
| # --- Type fallbacks rules_cc_autoconf doesn't expose macros for --- |
| # AC_C_VARARRAYS, AC_C_STRINGIZE: universal in C99+ toolchains. |
| checks.AC_DEFINE("HAVE_C_VARARRAYS", 1), |
| checks.AC_DEFINE("HAVE_STRINGIZE", 1), |
| # AC_C_BOOL (gl_C_BOOL): probes the C23 native `bool` keyword. Under |
| # `-std=gnu11` the keyword is unavailable; leaving HAVE_C_BOOL undef |
| # makes configh.in fall back to `#include <stdbool.h>` (which our |
| # header probe sets HAVE_STDBOOL_H=1 for). |
| # STDC_HEADERS: legacy macro; always emit on hosted environments. |
| checks.AC_DEFINE("STDC_HEADERS", 1), |
| # --- Misc --- |
| # ssize_t exists on POSIX; configh.in falls back to `int` if undefined. |
| # AC_HEADER_SYS_WAIT: emit HAVE_SYS_WAIT_H via the header probe above. |
| # socklen_t: leave unset; configh.in has its own `#undef socklen_t` slot. |
| # AC_SUBST for pkgextensiondir / EXTENSIONDIR — Bazel-controlled at |
| # runtime via gawk_tool's runfiles layout; no autoconf wiring needed. |
| # `HAVE_SOCKETS` (m4_socket): mirror upstream `GAWK_AC_LIB_SOCKETS`, |
| # which gates on `getaddrinfo` availability. Already probed above |
| # via `AC_CHECK_FUNCS`, so reuse the cache var. |
| # |
| # Use `requires=` (not `condition=`) so the macro is either |
| # `#define HAVE_SOCKETS 1` or absent entirely. With `condition=` |
| # it would emit `#define HAVE_SOCKETS 0` when the probe failed, |
| # and gawk source does `#if defined(HAVE_SOCKETS)` (true for any |
| # defined value) — that would drag `missing_d/getaddrinfo.c` |
| # into the compile via `replace.c` on toolchains where the |
| # probe can't link `getaddrinfo` standalone (e.g. MSVC, where |
| # it lives in ws2_32 that isn't linked at probe time). |
| checks.AC_DEFINE( |
| "HAVE_SOCKETS", |
| 1, |
| requires = ["ac_cv_func_getaddrinfo==1"], |
| ), |
| ] + |
| # --- Platform / flag-gated defines (formerly separate `m4_*` targets) --- |
| # `USE_PERSISTENT_MALLOC` (m4_pma): PMA is Linux-only upstream. |
| # |
| # We split this into two pieces — an `M4_VARIABLE` that's always |
| # emitted (so `autoconf_srcs` below can gate `support/pma.c` on it |
| # at analysis time on every platform) plus a conditional `AC_DEFINE` |
| # that only emits the actual `#define` when PMA is enabled. Without |
| # the always-present cache var, `autoconf_srcs` raises an analysis |
| # error on macOS / Windows complaining that the gate var doesn't |
| # exist. |
| select({ |
| ":_disable_pma_setting": [checks.M4_VARIABLE("gawk_cv_use_pma", 0)], |
| "@platforms//os:linux": [checks.M4_VARIABLE("gawk_cv_use_pma", 1)], |
| "//conditions:default": [checks.M4_VARIABLE("gawk_cv_use_pma", 0)], |
| }) + [ |
| # `requires=` (not `condition=`) is what GATES whether the |
| # define is emitted at all. `condition=` would still emit |
| # `#define USE_PERSISTENT_MALLOC 0` when the gate var is 0, |
| # and gawk source does `#ifdef USE_PERSISTENT_MALLOC` (true |
| # for any defined value) — linking would then fail with |
| # undefined `pma_*` symbols on non-Linux because |
| # `support/pma.c` isn't built. |
| checks.AC_DEFINE( |
| "USE_PERSISTENT_MALLOC", |
| 1, |
| requires = ["gawk_cv_use_pma==1"], |
| ), |
| ] + |
| # `HAVE_MPFR` (m4_mpfr): paired with the `:mpfr` label_flag. |
| select({ |
| ":_enable_mpfr_setting": [checks.AC_DEFINE("HAVE_MPFR", 1)], |
| "//conditions:default": [], |
| }) + |
| # `HAVE_LIBREADLINE` (m4_readline): paired with the `:readline` label_flag. |
| select({ |
| ":_enable_readline_setting": [checks.AC_DEFINE("HAVE_LIBREADLINE", 1)], |
| "//conditions:default": [], |
| }) + |
| # --- Boolean flag → define wiring via select() --- |
| # AC_BUILD_SETTING would render `False`/`True` literally; select() lets |
| # us emit a real `#define X 1` only when the flag is on. |
| select({ |
| ":_disable_lint_setting": [checks.AC_DEFINE("NO_LINT", 1)], |
| "//conditions:default": [], |
| }) + |
| select({ |
| ":_builtin_intdiv0_setting": [checks.AC_DEFINE("SUPPLY_INTDIV", 1)], |
| "//conditions:default": [], |
| }) + |
| select({ |
| ":_disable_extensions_setting": [], |
| # MSVC has no `<dlfcn.h>` and gawk's `pc/` MinGW-only dlopen |
| # shim uses GCC-isms we can't compile. Force-disable DYNAMIC |
| # on MSVC regardless of the `:disable_extensions` flag — |
| # extensions themselves are already gated incompatible with |
| # MSVC by `gawk_builtin_extension`'s `target_compatible_with`. |
| "@rules_cc//cc/compiler:msvc-cl": [], |
| "//conditions:default": [checks.AC_DEFINE("DYNAMIC", 1)], |
| }) |
| ), |
| deps = [":gawk_package_info"], |
| ) |
| |
| # --------------------------------------------------------------------------- |
| # Generate config.h from upstream configh.in. |
| # --------------------------------------------------------------------------- |
| |
| autoconf_hdr( |
| name = "config_h", |
| # Place under a subdir so the matching `cc_library` can use |
| # `strip_include_prefix` — bazel forbids `includes = ["."]` when the |
| # overlay is loaded as the root workspace (BCR presubmit's setup), and |
| # `includes = ["gawk_config"]` adds the dir to the global include path |
| # where it would shadow / be shadowed by `config.h` files shipped by |
| # other modules (e.g. mpfr / gmp ship their own at their package root). |
| out = "gawk_config/config.h", |
| template = "configh.in", |
| deps = [":configure_ac"], |
| ) |
| |
| # --------------------------------------------------------------------------- |
| # Conditional sources gated on probe / flag results. |
| # --------------------------------------------------------------------------- |
| |
| autoconf_srcs( |
| name = "gawk_conditional_srcs", |
| srcs = { |
| # Gate on the always-emitted `gawk_cv_use_pma` cache var rather |
| # than the conditionally-emitted `USE_PERSISTENT_MALLOC` define |
| # — autoconf_srcs evaluates the gate at analysis time before |
| # the per-platform `select()` for the define is resolved, so a |
| # gate var that doesn't exist on non-Linux raises an error. |
| "support/pma.c": "gawk_cv_use_pma", |
| }, |
| deps = [":configure_ac"], |
| ) |
| |
| # --------------------------------------------------------------------------- |
| # cc_library / cc_binary targets. |
| # --------------------------------------------------------------------------- |
| |
| # MSVC compatibility shims. Upstream gawk doesn't officially support MSVC, |
| # but with one targeted patch (`patches/awk_h_msvc_stdc_exempt.patch` — |
| # exempts `_MSC_VER` from `awk.h`'s `__STDC__` hard-error) plus two shim |
| # headers, the base interpreter compiles. Both shim headers are real |
| # `.h` files in the overlay (not `write_file`-generated) for IDE/syntax |
| # support; see [msvc_shim.h](msvc_shim.h) and |
| # [msvc_compat/langinfo.h](msvc_compat/langinfo.h) for the details. |
| cc_library( |
| name = "_msvc_shim", |
| hdrs = ["msvc_shim.h"], |
| visibility = ["//visibility:private"], |
| ) |
| |
| cc_library( |
| name = "_msvc_langinfo_stub", |
| hdrs = ["msvc_compat/langinfo.h"], |
| strip_include_prefix = "msvc_compat", |
| visibility = ["//visibility:private"], |
| ) |
| |
| # MSVC-style frontends (`msvc-cl` and `clang-cl`): use the `/`-style |
| # flags. `/w` silences all warnings, `/std:c11` enables C11. The shim |
| # is force-included so it lands before any other `#include` (in |
| # particular before `gawkapi.h` / `support/regex.h`). |
| _MSVC_STYLE_COPTS = [ |
| "/w", |
| "/std:c11", |
| "/FImsvc_shim.h", |
| ] |
| |
| # GCC-style frontends (`gcc`, `clang`, `mingw-gcc`, `emscripten`, |
| # anything else): `-w` silences all warnings; `-std=gnu11` keeps |
| # clang's relaxed C99/C11 rules so implicit-function-declaration |
| # stays a warning rather than the hard error clang ≥ 16 emits in |
| # strict ISO mode (gawk's vendored regex relies on this for |
| # `_REGEX_NELTS`, conditionally defined via the include chain). |
| _GCC_STYLE_COPTS = [ |
| "-w", |
| "-std=gnu11", |
| ] |
| |
| GAWK_COPTS = select({ |
| "@rules_cc//cc/compiler:clang-cl": _MSVC_STYLE_COPTS, |
| "@rules_cc//cc/compiler:msvc-cl": _MSVC_STYLE_COPTS, |
| "//conditions:default": _GCC_STYLE_COPTS, |
| }) |
| |
| # Deps fragment for targets that pull in the MSVC shim. Add this to the |
| # `implementation_deps` of every cc_library / cc_binary that compiles |
| # `awk.h`-touching source on MSVC so `/FI msvc_shim.h` resolves. |
| MSVC_SHIM_DEPS = select({ |
| "@rules_cc//cc/compiler:msvc-cl": [ |
| ":_msvc_shim", |
| ":_msvc_langinfo_stub", |
| ], |
| "//conditions:default": [], |
| }) |
| |
| LOCAL_DEFINES = [ |
| "GAWK", |
| "HAVE_CONFIG_H", |
| ] + select({ |
| # FS-layout defines are OS-level (both MinGW and MSVC builds for Windows |
| # use the same target paths). |
| "@platforms//os:windows": [ |
| 'LOCALEDIR="\\\"c:/gnu/share/locale\\\""', |
| 'DEFLIBPATH="\\\"c:/gnu/lib/gawk\\\""', |
| 'SHLIBEXT="\\\"dll\\\""', |
| # `DEFPATH` is referenced from `posix/gawkmisc.c:51`, which MSVC |
| # falls through to (it has no `__MINGW32__` branch). Without it |
| # the compile errors out with "DEFPATH undeclared identifier". |
| 'DEFPATH="\\\"c:/gnu/share/awk\\\""', |
| ], |
| "//conditions:default": [ |
| 'LOCALEDIR="\\\"/usr/local/share/locale\\\""', |
| 'DEFLIBPATH="\\\"/usr/local/lib/gawk\\\""', |
| 'SHLIBEXT="\\\"so\\\""', |
| 'DEFPATH="\\\".:/usr/local/share/awk\\\""', |
| ], |
| }) + select({ |
| # MinGW-specific runtime-library tweak; MSVC has its own stdio formatting. |
| "@rules_cc//cc/compiler:mingw-gcc": ["__USE_MINGW_ANSI_STDIO=1"], |
| "//conditions:default": [], |
| }) |
| |
| # Note: we used to predefine `__STDC__=1` on MSVC to satisfy `awk.h`'s |
| # hard-error check, but that made MSVC's `<sys/stat.h>` hide its POSIX |
| # `struct stat` alias and forced a cascade of `#define stat _stat` / |
| # `S_IF*` aliasing in the shim. `patches/awk_h_msvc_stdc_exempt.patch` |
| # exempts `_MSC_VER` from the check (mirroring the existing SunPro |
| # carve-out), so the predefine is no longer needed. |
| |
| filegroup( |
| name = "missing_d", |
| srcs = [ |
| "missing_d/strcoll.c", |
| "missing_d/strftime.c", |
| "missing_d/strncasecmp.c", |
| # `strsignal.c` is textually included by `replace.c` when |
| # `HAVE_STRSIGNAL` is not defined (true on MSVC; glibc and |
| # Apple libc both provide it). Listed unconditionally because |
| # this filegroup is the textual-headers set passed to `:awk`. |
| "missing_d/strsignal.c", |
| "missing_d/timegm.c", |
| ], |
| visibility = ["//visibility:public"], |
| ) |
| |
| cc_library( |
| name = "gettext", |
| hdrs = ["gettext.h"], |
| visibility = ["//visibility:public"], |
| ) |
| |
| cc_library( |
| name = "gawkapi", |
| hdrs = ["gawkapi.h"], |
| visibility = ["//visibility:public"], |
| ) |
| |
| cc_library( |
| name = "nonposix", |
| hdrs = ["nonposix.h"], |
| visibility = ["//:__subpackages__"], |
| ) |
| |
| cc_library( |
| name = "custom", |
| hdrs = ["custom.h"], |
| visibility = ["//visibility:public"], |
| ) |
| |
| # Internal — consumers (`:support`, `:awk`, `:gawk`, extensions) wire this in |
| # via `implementation_deps` and set `HAVE_CONFIG_H` themselves with |
| # `local_defines`. This prevents `config.h` and its `HAVE_*` macros from |
| # leaking into transitive consumers (e.g. downstream `cc_binary`s that depend |
| # on `:awk`). |
| cc_library( |
| name = "config", |
| hdrs = [":config_h"], |
| # `strip_include_prefix` keeps the header inside a per-library |
| # `_virtual_includes/config/` directory so it can't be shadowed by |
| # `config.h` shipped by other modules (mpfr, gmp, ...). |
| strip_include_prefix = "gawk_config", |
| visibility = ["//visibility:public"], |
| deps = [":custom"], |
| ) |
| |
| cc_library( |
| name = "support_hdrs", |
| hdrs = glob([ |
| "support/*.h", |
| "support/malloc/*.h", |
| ]) + [ |
| "support/malloc/dynarray-skeleton.c", |
| "support/regcomp.c", |
| "support/regex_internal.c", |
| "support/regexec.c", |
| ], |
| strip_include_prefix = "support", |
| ) |
| |
| cc_library( |
| name = "support", |
| srcs = [ |
| "support/dfa.c", |
| "support/getopt.c", |
| "support/getopt1.c", |
| "support/localeinfo.c", |
| "support/malloc/dynarray_at_failure.c", |
| "support/malloc/dynarray_emplace_enlarge.c", |
| "support/malloc/dynarray_finalize.c", |
| "support/malloc/dynarray_resize.c", |
| "support/malloc/dynarray_resize_clear.c", |
| "support/minrx.c", |
| "support/random.c", |
| "support/regex.c", |
| ] + [":gawk_conditional_srcs"], |
| hdrs = glob([ |
| "support/*.h", |
| "support/malloc/*.h", |
| ]) + [ |
| "support/malloc/dynarray-skeleton.c", |
| "support/regcomp.c", |
| "support/regex_internal.c", |
| "support/regexec.c", |
| ], |
| copts = GAWK_COPTS, |
| implementation_deps = [ |
| ":config", |
| ":support_hdrs", |
| ] + MSVC_SHIM_DEPS, |
| includes = [ |
| "support", |
| "support/malloc", |
| ], |
| local_defines = LOCAL_DEFINES, |
| # `support/minrx.c` textually `#include`s `support/charset.c`; the |
| # included file must be visible to the compile action. |
| textual_hdrs = ["support/charset.c"], |
| deps = [":gettext"], |
| ) |
| |
| cc_library( |
| name = "awk", |
| srcs = glob( |
| ["*.c"], |
| exclude = ["main.c"], |
| ), |
| hdrs = glob(["*.h"]) + [":missing_d"], |
| copts = GAWK_COPTS, |
| implementation_deps = [":config"] + MSVC_SHIM_DEPS, |
| includes = ["./support"], |
| linkopts = select({ |
| "@platforms//os:linux": ["-ldl"], |
| "@platforms//os:macos": ["-ldl"], |
| "//conditions:default": [], |
| }) + select({ |
| # MinGW-only: `-lws2_32` for winsock (`io.c`'s socket paths are |
| # `__MINGW32__`-gated; MSVC has `HAVE_SOCKETS` disabled and needs |
| # nothing), plus `-Wl,-allow-multiple-definition` (GNU-ld-only |
| # papering over multi-def warnings carried from the pre-autoconf |
| # overlay). Both flags are rejected by MSVC's `link.exe` |
| # (`LNK4044: unrecognized option`). |
| "@rules_cc//cc/compiler:mingw-gcc": [ |
| "-lws2_32", |
| "-Wl,-allow-multiple-definition", |
| ], |
| "//conditions:default": [], |
| }), |
| local_defines = LOCAL_DEFINES, |
| # gawkmisc.c does `#include "posix/gawkmisc.c"` on POSIX and |
| # `#include "pc/gawkmisc.pc"` / `#include "pc/mbc32.c"` on MinGW. These |
| # are textually-included sources from the upstream tarball; bazel needs |
| # them declared somewhere to make them visible to the compile action. |
| # The pc/ implementations only compile under MinGW (gawkmisc.c gates on |
| # `__MINGW32__`); MSVC builds would need separate code paths that |
| # upstream gawk doesn't provide. |
| textual_hdrs = ["posix/gawkmisc.c"] + select({ |
| "@rules_cc//cc/compiler:mingw-gcc": glob([ |
| "pc/*.c", |
| "pc/*.h", |
| ]) + ["pc/gawkmisc.pc"], |
| "//conditions:default": [], |
| }), |
| visibility = ["//visibility:public"], |
| # `:config` is an `implementation_deps` so `config.h` (and its `HAVE_*` |
| # macros) stay scoped to `:awk`'s own compilation and don't leak into |
| # transitive consumers. `awk.h` gates its `#include <config.h>` behind |
| # `#ifdef HAVE_CONFIG_H` and we only set that as a `local_defines` on |
| # this library, so downstream `cc_binary`s that depend on `:awk` never |
| # try to resolve `<config.h>` from their own include path — no chance |
| # of colliding with a `config.h` shipped by a sibling cc_library. |
| # |
| # Caveat: a user who turns on `:enable_mpfr` and points `:mpfr` at a |
| # provider that itself exposes `<config.h>` at its include root may see |
| # `<config.h>` resolve to the provider's instead of gawk's during |
| # `:awk`'s own compilation, because `implementation_deps` come AFTER |
| # `deps` in the resolved include path. Pick (or wrap) a provider that |
| # doesn't expose its own `config.h` to fix this on the user side. |
| # |
| # `:mpfr` and `:readline` are label_flags. When the matching |
| # `:enable_<lib>` bool_flag is False (the default) nothing is added; |
| # when True the user-supplied label is pulled in (or the `#error` |
| # sentinel cc_library, which fails the compile with a helpful |
| # message pointing at the flag to set). |
| deps = [":support"] + select({ |
| ":_enable_mpfr_setting": [":mpfr"], |
| "//conditions:default": [], |
| }) + select({ |
| ":_enable_readline_setting": [":readline"], |
| "//conditions:default": [], |
| }), |
| ) |
| |
| cc_binary( |
| name = "gawk", |
| srcs = ["main.c"], |
| copts = GAWK_COPTS, |
| local_defines = LOCAL_DEFINES, |
| visibility = ["//visibility:public"], |
| # `:config` is needed for `main.c`'s compile (awk.h's |
| # `#ifdef HAVE_CONFIG_H` arm). `cc_binary` has no `implementation_deps` |
| # so it lives here; this is the end of the dep chain so nothing |
| # transitively re-exports config.h. |
| deps = [ |
| ":awk", |
| ":config", |
| ] + MSVC_SHIM_DEPS, |
| ) |
| |
| # `:gawk` rebuilt under a transition that forces every optional `bool_flag` |
| # and `label_flag` back to its safe default. Downstream modules that need |
| # gawk at their own build time (e.g. `ncurses` generating `term.h` with it) |
| # should depend on this target rather than `:gawk` directly so a user's |
| # `--//:enable_mpfr=true` (or label override) can't drag MPFR / readline |
| # into a configuration that can't satisfy them. |
| gawk_minimal( |
| name = "gawk_minimal", |
| visibility = ["//visibility:public"], |
| ) |