This is an automated email from the ASF dual-hosted git repository. leginee pushed a commit to branch bazel-migration in repository https://gitbox.apache.org/repos/asf/openoffice.git
commit 3ce2cfc700a2d2b5916ada5c5d44fee8585da995 Author: Peter Kovacs <[email protected]> AuthorDate: Mon Jun 22 23:29:10 2026 +0200 update harness --- CLAUDE.md | 3 + STYLEGUIDE.md | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index f9ecc852fe..f56f7a43cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -138,6 +138,9 @@ Source code is NOT being changed — only the build system. - BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1 disables auto-detection ## Key conventions +- BUILD-file authoring conventions (module skeleton, naming, idioms, anti-patterns): + see STYLEGUIDE.md. The rules below are the operating/landmine notes; STYLEGUIDE.md + is the copy-paste pattern reference for new modules. - BUILD.bazel files live at main/<package>/BUILD.bazel (NOT prj/) - prj/ convention only worked for nmake() wrappers; cc_library needs glob() access to sources which requires the BUILD at module root diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md new file mode 100644 index 0000000000..10196c63fc --- /dev/null +++ b/STYLEGUIDE.md @@ -0,0 +1,216 @@ +# BUILD-file Styleguide — AOO Bazel Migration + +How to author `BUILD.bazel` and `.bzl` files so a new module looks like the +ones already migrated. This is the **code** styleguide for this repo — the +actual C++/Java source is out of scope (see [CLAUDE.md](CLAUDE.md) "Goal"). + +For *operating* rules (build commands, workflow, guardrails) see +[CLAUDE.md](CLAUDE.md). For *why* a given pattern exists, the +`MEMORY.md` milestone for that module is the primary record. This file is +the **generalization** of those ~50 milestones into copy-pasteable shape. + +Canonical reference example to imitate: [main/basctl/BUILD.bazel](main/basctl/BUILD.bazel) +(a full first-party DLL with SDI, RSC, shielded includes, implib export). +Foundational library example: [main/sal/BUILD.bazel](main/sal/BUILD.bazel). + +--- + +## 1. Module BUILD skeleton + +A first-party module DLL is built in this order. Copy the shape, not the names. + +```python +package(default_visibility = ["//visibility:public"]) + +load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") +# load only the pipelines this module actually uses (rsc_res, sdi_target, ...) + +# 1. Defines and copts as module-local _DEFINES / _COPTS lists (see §2) +_DEFINES = [ "WNT", "GUI", "WIN32", ... , "snprintf=_snprintf", "SUPD=680" ] +_COPTS = [ "/Zc:wchar_t-", "/Imain/<mod>/inc", "/Imain/<mod>/source/inc", ... ] + +# 2. Header / include "shield" libraries (see §3) +cc_library(name = "<mod>_inc", textual_hdrs = glob(["inc/**"]), strip_include_prefix = "inc") +cc_library(name = "<mod>_source_inc", textual_hdrs = glob(["source/inc/**"]), strip_include_prefix = "source/inc") + +# 3. Generated inputs (SDI slots, IDL, etc.) — only if the module uses them +sdi_target(name = "<mod>slots_sdi", ...) +cc_library(name = "<mod>slots_headers", hdrs = [":<mod>slots_sdi"], includes = ["<mod>slots_sdi_inc"]) + +# 4. The DLL itself +cc_binary( + name = "<mod>", + srcs = ["inc/pch/precompiled_<mod>.cxx"] + glob(["source/**/*.cxx", "source/**/*.hxx"]), + copts = _COPTS, + defines = _DEFINES, + linkshared = True, + win_def_file = "util/<mod>.def", # see §5 + deps = [ ":<mod>_inc", ... , "//main/sal:sal_headers", ... ], # headers only + additional_linker_inputs = [ "//main/sal:sal_implib", ... ], # implibs (see §5) + linkopts = [ "$(execpath //main/sal:sal_implib)", ... , "shell32.lib" ], +) + +# 5. Re-export this module's own implib for downstream consumers (see §5) +filegroup(name = "<mod>_implib", srcs = [":<mod>"], output_group = "interface_library") + +# 6. Resources / config, if any (see §6) +rsc_res(name = "<mod>_res", ...) +filegroup(name = "uiconfig_files", srcs = glob(["uiconfig/**/*.xml"]), visibility = ["//main/postprocess:__pkg__"]) +exports_files(glob(["**/*.component"])) +``` + +**Driving the dep graph:** parse the module's legacy `build.lst` / +`makefile.mk` to derive `deps`. `SLOFILES`/`OBJFILES` lists tell you exactly +which sources belong in the library — prefer matching them over a blind +`glob`, and `exclude` the ones a glob over-collects (see `main/sal` rtl: it +excludes `gen_makefile.cxx`, `alloc_fini.cxx`, `strtmpl.c` because they are +not in `SLOFILES`). + +--- + +## 2. Defines & copts conventions + +- Hoist into module-local `_DEFINES` and `_COPTS` lists at the top of the file + so every target in the module shares them. Don't inline per-target. +- The cross-cutting flags (`/Zc:wchar_t-`, `snprintf=_snprintf`, + `stlport` dep, `Z_PREFIX`, `CURL_STATICLIB`, …) are catalogued in + [CLAUDE.md](CLAUDE.md) "Cross-cutting compiler flags & defines". **Check that + list before building any new module** — most "mysterious" link/ABI failures + are a missing entry from it. +- `_HAS_ITERATOR_DEBUGGING=0` is set **globally** in the toolchain — do **not** + re-add it per module (redundant; see CLAUDE.md). +- Use a `_SLO_DEFINES = _DEFINES + ["SHAREDLIB", "_DLL_"]` derived list when a + module builds both shared- and static-lib object sets (see `main/sal`). + +--- + +## 3. Idioms to copy + +### Shield (`_inc`) libraries — beat header-shadowing by name +Many modules have identically-named headers (`helpid.hrc`, `*sh.hrc`). Expose a +module's own `inc/` and `source/inc/` as **separate** `cc_library` targets with +`strip_include_prefix`, listed **first** in `deps`, so they resolve before any +transitively-added same-named header. Use `textual_hdrs` (not `hdrs`) for +`.hrc`/`.tab`/`#include`d data files so Bazel doesn't try to compile-check them. +Reference: [main/basctl/BUILD.bazel](main/basctl/BUILD.bazel) `basctl_inc` / +`basctl_source_inc`, and the cui/basctl milestones in `MEMORY.md`. + +### Per-subdir `/I` copts for flat sibling includes +Source that includes a sibling header by bare name (`<baside2.hrc>`, +`"precompiled_sal.hxx"`) needs that exact directory on the include path. Add a +`/Imain/<mod>/<subdir>` copt per such subdir (basctl adds `/Imain/basctl/source/basicide`). + +### `_headers` libraries are the public contract +A module exposes its API to others as `//main/<mod>:<mod>_headers` (headers + +`strip_include_prefix` + `includes`), never by depending on its `cc_binary`. +Downstream `deps` carry **only** `_headers`; linking is via implibs (§5). + +### Generated headers get a wrapper `cc_library` +SDI/IDL/MIDL output is wrapped: `hdrs = [":<gen>"]`, `includes = +["<gen>_inc"]` (or `includes = ["."]` / `[".."]` for generated-header dirs). +Reference: `basslots_headers`, and the winaccessibility/writerfilter milestones. + +### `textual_hdrs` for included data tables +`.tab` files (`main/sal` textenc), `hash.cxx` (sdext pdfimport), private +`types.idl`-derived headers — anything `#include`d rather than compiled — goes +in `textual_hdrs`. + +--- + +## 4. Naming conventions + +| Target | Pattern | Example | +|---|---|---| +| Public header lib | `<mod>_headers` | `sal_headers`, `tools_headers` | +| Private include shield | `<mod>_inc`, `<mod>_source_inc` | `basctl_inc` | +| Resource-constant shield | `<mod>_source_hrc`, `<mod>_hrc` | `basctl_source_hrc`, `svl_hrc` | +| The DLL | `<mod>` (bare) | `basctl`, `sal` | +| Re-exported implib | `<mod>_implib` (or `<linkname>_implib`) | `basctl_implib`, `comphelpMSC_implib` | +| SDI slot gen | `<name>slots_sdi` + `<name>slots_headers` | `basslots_sdi` | +| RSC resource | `<mod>_res` | `basctl_res` | +| DEF file | `util/<linkname>.def` | `util/basctl.def` | + +The **implib name often differs from the module name** because it matches the +upstream link-name, not the directory (`comphelpMSC_implib`, `tl_implib`, +`utl_implib`, `svxcore_implib`). When in doubt, check the consumer's +`additional_linker_inputs` list. + +--- + +## 5. The DLL link triad (DEF + implib in + implib out) + +A first-party DLL has three linking concerns that must stay consistent: + +1. **Exports** — convert the upstream `<name>.map` GNU-ld script to a Windows + `util/<name>.def` and set `win_def_file`. UNO component DLLs export exactly + `component_getImplementationEnvironment`, `component_getFactory`, + `component_canUnload`. If there is no `.map`, hand-write a minimal `.def` + (see starmath milestone: `sm.def`/`smd.def`, no `canUnload`). +2. **Consuming others' implibs** — list each dependency's implib in **both** + `additional_linker_inputs` (so Bazel stages the file) **and** `linkopts` as + `$(execpath //main/<dep>:<dep>_implib)`. Both are required; the + `additional_linker_inputs` entry alone won't put it on the link line. + Plain system libs (`shell32.lib`, `user32.lib`, `ws2_32.lib`) go straight + in `linkopts`. +3. **Exposing your own implib** — end the file with + `filegroup(name="<mod>_implib", srcs=[":<mod>"], output_group="interface_library")` + so downstream modules can link you. + +--- + +## 6. Resources, config, components + +- **RSC** (`rsc_res`): `srcs` = `.src`, `hdrs` = `.hrc` (own + cross-module + `*_inc_hrc` filegroups), `includes` = every `.hrc` search dir, and + **`images_root = "main/default_images"`** — this exact value is mandatory for + any module with `Bitmap { File = "x.png" }` (see CLAUDE.md; a module-specific + `images_root` silently empties every `BitmapEx(ResId)`). +- **uiconfig**: expose as a `filegroup` visible to `//main/postprocess` only; + it is staged as a **folder tree**, never a zip. +- **`.component`** files: `exports_files(glob(["**/*.component"]))` so the + postprocess/services.rdb pipeline can read them. + +--- + +## 7. `.bzl` custom rules + +When a build step has no `cc_*`/`genrule` fit, it gets a Starlark rule under +`build/rules/<thing>.bzl` (existing: `idl_pipeline`, `rsc_pipeline`, +`sdi_target`, `images`, `scp2`, `localedata_pipeline`, `gtest_test`, …). +Conventions, by example: + +- Prefer `ctx.actions.run` with explicit tool inputs over `run_shell`; use + `ctx.actions.symlink` (pure Bazel) for staging, not a shell `cp`. +- Tools that link `/MD` need their CRT DLLs + **external** `.manifest` staged + beside the EXE (the `svidl_bundle` pattern — replicated in `gtest_test.bzl`; + miss it and you get R6034). See the test-infra and IDL milestones. +- A rule that emits headers must advertise the include dir via an `includes` + provider/attr so the wrapper `cc_library` can surface it. +- Keep the rule's migration rationale in its module `readme.md` and a one-line + `MEMORY.md` pointer — that is where future-you will look. + +--- + +## 8. Anti-patterns (do NOT do) + +- ❌ Put a BUILD file in `prj/`. First-party modules need `glob()` over sources + → BUILD lives at `main/<mod>/BUILD.bazel`. +- ❌ Depend on another module's `cc_binary` to get its headers. Use `_headers`. +- ❌ Module-specific `images_root`. Always `"main/default_images"`. +- ❌ Re-add `_HAS_ITERATOR_DEBUGGING=0` per module (global already). +- ❌ Put an implib only in `additional_linker_inputs` (also needs `linkopts`). +- ❌ Stage uiconfig as a zip (must be a folder tree). +- ❌ Manipulate the Bazel cache directly. Use + `bazel mod deps --lockfile_mode=refresh` or ask the user. +- ❌ Modify C++/Java source to make a build pass. Source is out of scope; fix + the build description instead. (If a source bug is genuinely blocking, raise + it — see the latent-UAF precedent — don't silently patch.) + +--- + +## 9. After a module builds + +Per [CLAUDE.md](CLAUDE.md) workflow: **let the user build** (don't run the build +yourself), add a `readme.md` migration summary to the module, add a one-line +`MEMORY.md` milestone pointer, and **only then** update the frontier in +CLAUDE.md. The `MEMORY.md` entry is what makes the next similar module fast.
