Skip to content

C/C++ Build Rules

C/C++ compilation involves three distinct phases: preprocessing, compiling, and linking, each requiring specific compiler flags.

Source languages

srcs accepts C (.c), C++ (.cc/.cpp/.cxx), assembly (.s/.S/.asm), and Objective-C / Objective-C++ (.m / .mm). Objective-C(++) compiles through the same rules — the clang/gcc driver infers the language from the extension — and .mm is treated as C++ (gets cxxflags/extra_cxxflags). Link Apple frameworks via extra_linkflags, e.g. extra_linkflags=['-framework', 'Foundation']. The MSVC toolchain has no Objective-C support, so keep .m/.mm out of srcs on Windows (gate them with a blade.cc_toolchain conditional in the BUILD file).

Third-party libraries

Besides building dependencies from source, C/C++ targets can link prebuilt system libraries (#name, e.g. #pthread) and packages managed by vcpkg through vcpkg#<port>:<lib> dependencies. See Using vcpkg packages for the full guide.

Common C/C++ Attributes

warning: string = ['yes', 'no']

Controls whether to suppress all compiler warnings.

Example: warning='no' Default: 'yes' (can typically be omitted)

defs: string[] = []

User-defined preprocessor macros.

Example: defs=['_MT'] or defs=['A=1'] for macros with values

incs: string[] = []

Header file search paths.

Example: incs=['poppy/myinc'] Best Practice: Use full include paths in source code rather than relying on additional search paths

optimize: string[] = []

Compiler optimization flags.

Example: optimize=['-O3'] Rationale: Optimization flags are separated from extra_cppflags because they should be disabled in debug mode to preserve debugging capability. For mature, performance-critical libraries (e.g., hash, compression, cryptography) where debugging is rarely needed, set always_optimize = True.

extra_cppflags: string[] = []

Additional compilation flags applied to all C-family sources (C, C++ and assembly).

Example: extra_cppflags = ['-Wno-format-literal'] Note: Essential flags like -g and -fPIC are built-in, so this parameter should be used sparingly.

extra_cflags / extra_cxxflags / extra_asflags: string[] = []

Additional compilation flags applied to a single source language, in addition to extra_cppflags:

  • extra_cflags — C sources (.c)
  • extra_cxxflags — C++ sources (.cc, .cpp, .cxx)
  • extra_asflags — assembly sources (.s, .S, .asm)

Use these when a flag is only valid for one language (e.g. a C++-only -std= or warning) so it isn't passed to the others. They are selected per source file by extension.

Example: extra_cxxflags = ['-fno-rtti']

extra_linkflags: string[] = []

Additional linker flags.

Example: extra_linkflags = ['-fopenmp'] Note: Common flags like -g are already included, minimizing the need for this parameter.

linkflags: string[] = None

Overrides global linkflags configuration.

Example: linkflags = ['-fopenmp'] Caution: This parameter overrides global settings. Use only with thorough understanding of GCC and linker options.

cc_library

Build a C/C++ library

cc_library can be used to build static and dynamic library. But only the static library will built default, unless it is depended by a dynamic_linked cc_binary, or run blade build with --generate-dynamic.

Example:

cc_library(
    name='lowercase',
    srcs=['lower/plowercase.cpp'],
    hdrs=['lower/plowercase.h'],
    deps=['#pthread'],
    link_all_symbols=False
)

Attributes:

  • hdrs: list

Declares the public interface header files of the library.

For normal CC libraries, hdrs should exist, otherwise the library may not be used. Therefore, this attribute is required, otherwise a diagnostic problem will be reported. If it surely does not exist, you should set it to empty([]) explicitly.

The severity of the problem can be controlled by cc_library_config.hdrs_missing_severity. For problems that existed before hdrs support, you can use cc_library_config.hdrs_missing_suppress to suppress them.

Rules for generating header files during construction, such as pb.h generated by proto_library or outs of the gen_rule target, if these header files are included, these header files will also be automatically included. Including the header file in the dependency management can avoid compilation or linking problems caused by the library that includes the header file but does not add the dependency, especially for dynamically generated header files.

A header file can belong to multiple cc_library. cc_library will not automatically export hdrs of other cc_library that it depends on in deps. Only public header files should be declared in hdrs. For private header files, even if they are included in public header files, they do not need to be included. Private header files should be declared in its srcs.

All CC libraries should be described by cc_library, especially for headers-only libraries. Because most library inevitably depends on other libraries, if dependency of the ordinary library is shortage, the "symbol not found" error will be reported during linking, it is easier to add missing dependencies based on error information, but for headers-only libraries, even indirect dependencies, errors are reported at the linking time of the final user, making it difficult to locate and fix the problem.

Therefore, for header-only libraries, they also needs to be described with cc_library, its public header files need to be declared in its hdrs, and its direct dependencies need to be listed in its deps.

If the granularity of the library is too large, some unnecessary dependencies will be included due to the enforcement of the hdrs checking. You should proper splitting the library to reduce unnecessary coupling.

For example, gtest_prod.h of gtest is often used to declare testing support in product code, but it only contains some declarations and does not depend on the implementation part of the gtest library. It is suitable to declare it in a separate gtest_prod library instead of putting it into gtest library, Otherwise, the gtest library may be linked into the product code.

  • textual_hdrs: list = []

Header-like files that are exposed to dependents and #included textually, but are never compiled or parsed on their own. This is the analog of Bazel's textual_hdrs.

Unlike hdrs — which must be self-contained, are checked for a header file extension, and are each preprocessed standalone to build the inclusion- dependency graph — textual_hdrs are exempt from all of that. They may have any extension and need not be valid in isolation. Like hdrs, they are declared as provided by this library, so a dependent that #includes one passes the missing-dependency check (and the dependency counts as used).

Use it for files that only make sense pasted into the middle of another translation unit:

  • X-macro / token lists — a file #included repeatedly with different macro definitions to expand a list (no include guard, not standalone):

    cc_library(
        name = 'opcodes',
        srcs = ['vm.cc'],          # vm.cc: #define OP(x) ...; #include "opcodes.def"
        hdrs = ['vm.h'],
        textual_hdrs = ['opcodes.def'],
    )
    
  • Platform-dispatch fragments — one source #includes the right implementation fragment per platform, so the fragments are not compiled directly:

    cc_library(
        name = 'event_loop',
        # event_loop.cc: #if __linux__  #include "event_loop_epoll.inc" ...
        srcs = ['event_loop.cc'],
        hdrs = ['event_loop.h'],
        textual_hdrs = ['event_loop_epoll.inc', 'event_loop_kqueue.inc'],
    )
    

A file #included textually but listed in hdrs instead would fail: blade rejects a non-header extension, and otherwise tries to preprocess it on its own — which errors on a fragment that references symbols/macros only defined by its includer. textual_hdrs is the correct home for such files.

  • link_all_symbols: bool = False

If you depends on the global initialization to register something, but didn't access these global object directly. it works for code in srcs of executable. but if the global is in a library, it will be discarded at the link time because linker find it is unsed.

This attribute tells the linker don't discard any unused symbols in this library even if it seems to be unused.

You'd better put these self-registering code into a separated small library. otherwise, the whole library will be linked into the executable unconditionally, increase the size of the executable.

Youi also need to know, the link_all_symbols is the attribute of the library, not the user of it. Click here to learn more details if you have interesting.

  • generate_dynamic: bool | None = None

Whether a shared library (.so/.dylib/.dll) is generated for this library in addition to the static one.

The default None means "decide automatically": the shared library is generated only when the library is depended on by a dynamic_link executable, or when building with --generate-dynamic. The static library is always generated regardless.

Setting generate_dynamic = False opts the library out permanently: no shared library is ever generated, and the library is linked statically even into a dynamic_link executable. This is the right choice for libraries that expose global mutable data across the link boundary (for example a test framework with a global test registry), because such data is not safe to share through an auto-exported shared library — see Windows DLL support below.

Setting generate_dynamic = True forces the shared library to be generated unconditionally.

  • check_undefined: bool = True (EXPERIMENTAL)

Whether Blade statically validates — at archive time, before any link — that the target's declared deps cover every undefined symbol the library references. If a symbol is left unresolved, the check reports a diagnostic pointing at the missing dep, instead of waiting for a final link to fail (often hundreds of targets later) with an opaque linker error.

The check uses nm (or dumpbin on MSVC) on the target's just-built static archive and each transitive cc_library dep's archive, plus pre-generated symbol caches for every #alias system library (so e.g. consumers of pow() are required to actually declare '#m'). It runs even when generate_dynamic = True — it is faster than the link itself and catches the same misses earlier, per-library, with feedback that points at the specific dep instead of at the final binary.

The check ships enabled by default, but while it is still experimental its findings default to warning severity (see cc_library_config.check_undefined_severity) — the build keeps going so any edge cases surface as diagnostics rather than CI failures. Flip the severity to error once it's clean on your codebase.

See Static undefined-symbol check for the bigger picture; per-invocation overrides are --cc-check-undefined / --no-cc-check-undefined; the global default is cc_library_config.check_undefined.

  • allow_undefined: bool | list[str] = False

Allows the target to reference symbols that no declared dep provides — i.e. the symbols will be resolved at the final link by something outside the dep graph (typical case: a plugin loaded by a host process that supplies the host's symbols).

  • False (default) — every undefined symbol must be covered by deps; the static check enforces this.
  • True — opt out entirely: skip the static check and pass -Wl,--no-undefined's opposite (i.e. don't assert closure at link time either). Use this for plugin-style libraries that are loaded by an executable supplying the missing symbols.
  • list[str] — a narrowed allowlist of regex patterns matched against the mangled name (what nm -u prints). The static check tolerates exactly the listed names while still enforcing closure for everything else.

This is per-target. A project-wide allowlist (matching the same regex semantics) lives in cc_library_config.allow_undefined.

  • lto: bool (default: follow the build)

Opt this target out of Link-Time Optimization by setting lto = False: its sources compile native (no -flto) and link as ordinary objects alongside the bitcode of the rest of the build. Use it for a translation unit that miscompiles under LTO, or a library that must stay native. Only False is meaningful — there is no per-target full/thin selection, because the link strategy is one choice for the whole build. Whether LTO is on at all is the project policy cc_config.lto (overridable with --lto / --lto=no); when LTO is off, this attribute has no effect.

  • binary_link_only: bool = False

This library can only be a depenedency of the executable targets (Such as cc_binary or cc_test), but not normal cc_librarys. This attribute is useful for some exclusive libraries such as malloc library.

For example, tcmalloc and jemalloc are both malloc library which contains same symbols (such as malloc, free), if a cc_library depends on tcmalloc, the dependent cc_binary may not depends on jemalloc any more, otherwise linker will report multiple malloc definition. This problem can be solved by setting this attribute on both 'tcmalloc' and 'jemalloc'.

A 'binary_link_only' library can depends on other 'binary_link_only' libraries.

cc_library(
    name = 'tcmaloc',
    binary_link_only = True,
    ...
)

cc_library(
    name = 'jemaloc',
    binary_link_only = True,
    ...
)
  • always_optimize: bool = False

True: Always optimize. False: Don't optimize in debug mode. The default value is False.It only apply to cc_library.

  • prebuilt: bool = False

Use prebuilt in cc_library is deprecated. you should use prebuilt_cc_library.

  • export_incs: list = []

Similar to incs, but it is transitive for all targets depends on it, even if indirect depends on it.

NOTE:

There is no dependency code in the generated cc_library, both static and dynamic. But for the dynamic library, it contains the paths of dependencies.

These libraries can just be used locally (such as run tests),not fit for the production environment. If you want to build a shared library can be use in the environment, you should use cc_plugin, which will include the code from it dependencies.

  • system_include: bool = False

When True, consumers compile with -isystem <path> instead of -I <path> for this library's export_incs. The compiler treats those headers as system headers: diagnostics raised inside them are suppressed by default (and not promoted to errors by the consumer's -Werror). Use for thin wrappers around third-party / vendored / generated headers whose own warnings shouldn't bleed into first-party builds. foreign_cc_library is automatically treated this way; system_include = True is the opt-in for hand-written wrappers.

Fix missing dependencies errors caused by hdrs

In large-scale C++ projects, dependency management is very important, and header files have not been included in it for a long time. Starting with Blade 2.0, header files have also been included in dependency management. When a cc target includes a header file, it also needs to put the cc_library it belongs to in its own deps, otherwise Blade will check and report the problem.

The lack of a declaration on the library to which the header files belong will cause the following problems:

  • The dependencies between libraries cannot be transferred correctly. If the library to which an undeclared header file belongs adds new dependencies in the future, it may cause link errors.
  • For the header files generated during the build, the lack of a dependency declaration for the library to which they belong will cause these header files to have not yet been generated at compile time, resulting in compilation errors.
  • To make matters worse, if these header files already exist but have not been updated, outdated header files may be used during compilation, which will cause runtime errors that are more difficult to troubleshoot.

The severity of the problem can be controlled by the cc_config.hdr_dep_missing_severity configuration item. For problems that existed before hdrs were supported, It can be suppressed by cc_config.hdr_dep_missing_suppress.

Blade can detect two kind of missing dependencies:

  • Missing dependenvy

The header files are included in srcs or hdrs through the #include directive, but the library to which they belong is not declared in deps, or these header files are not declared in any hdrs of cc_library at all.

Before mechanically following the "add this dep" suggestion, pause and ask which of the patterns below actually applies — the mechanical fix is right in case (1), but in cases (6) and (7) it either bloats the dep graph for nothing or runs straight into a visibility error on the next build.

Problems and solutions:

  1. The library to which the header file belongs is not declared in the deps of this target, just follow the instruction to fix it.
  2. The header file is a private header file of the library to which it belongs, and direct use is prohibited. Exception: see case (7).
  3. The header file should be the public header file of this target, it should be declared in its hdrs.
  4. The header file should be the target's private header file, it should be declared in its srcs.
  5. The header file should be a public header file of other libraries, but there is no declaration, just declare it in the corresponding library's hdrs.
  6. The #include is unnecessary; just delete it. Blade's missing-dep check is content-blind — it only sees that you #include-d a header. If your source doesn't actually use any name from that header (the include was copy-pasted, became dead after a refactor, or the type you thought you needed turns out to be re-exported through a header you already include), the right fix is to remove the #include line, not to add a dep. A quick check: grep your source for the types / functions defined in the suspicious header; if nothing matches, the include is dead.
  7. Legitimate "facade" re-export across a visibility boundary. When an umbrella target's own public header re-#includes a sub-library's header so the umbrella's consumers can use those types, blade will report a Missing-dep notice on the umbrella because the sub-library is locked down via visibility. The umbrella IS a legitimate consumer — it's the canonical re-exporter — but case (2)'s "private, direct use prohibited" rule is overly absolute here. Treat the umbrella like a C++ friend: add it to the sub-library's visibility list (a narrow, named widening) and declare the sub-library in the umbrella's deps. Both the visibility entry and the dep declaration should mention the re-export so the relationship is self-documenting.

    # In the inner sub-library:
    cc_library(
        name = 'http_filter',
        hdrs = 'http_filter.h',
        ...,
        # `friend`-style allow-list: only the two umbrella facades may
        # depend on us directly; everyone else must go through them.
        visibility = ['//flare/rpc:http', '//flare/rpc:rpc'],
    )
    
    # In the umbrella whose public `hdrs` re-export the sub-library:
    cc_library(
        name = 'rpc',
        hdrs = ['http_filter.h', ...],  # re-includes inner header
        deps = [
            ...,
            '//flare/rpc/protocol/http:http_filter',  # we re-export it
        ],
        visibility = 'PUBLIC',
    )
    

    If the umbrella consumes the sub-library only for its public surface (not as a re-export through the umbrella's own hdrs), and unused_deps flags the dep, that's actually the Header re-export auto-exemption case in the other direction — declare the sub-library's header in the umbrella's hdrs and the unused-deps check stops complaining without any visibility widening.

  8. Missing indirect dependency

One of the indirect included header file(included in the header file) does not appear in the deps of this target and its transitive dependencies.

We only do this check for the header files generated during compilation. Because for the rules for generating header files (such as proto_library or possibly gen_rule), if the dependencies are missing, it may cause these header files to be ungenerated or outdated when compiling the current target, resulting in compilation errors. Fixing this error is a little more troublesome. You need to analyze the include stack reported by the error message, starting from the source file, and searching upwards in the library to which each header file belongs, whether it depends on the library to which the included header file belongs.

At this time, you may encounter a situation, that is, some libraries with pure header files have no implementation files, so there is no orresponding cc_library to describe it. To fix this issue, you need to write a new cc_library for it, add header files in the hdrs, add the implementation dependencies in its deps, and add it to the deps of which library depends on it.

This can solve the root cause, but it does require some effort. The simple and rude solution is to add the reported missing library to the current target deps, which is equivalent to relying on the implementation details of some libraries, which is not recommended.

Since Blade fully managed the dependency of header files, for any header files that are not declared in the hdrs or src of any library, an error will be reported after the build. If these header files should belong to the current target, According to whether it is public or private, add it to hdrs or srcs respectively. If it belongs to other libraries, it should be added to hdrs of other libraries, and you cannot include undeclared or private header files of other libraries.

For undeclared header files that already existed in the code base before the upgrade, you can use the cc_config.allowed_undeclared_hdrs configuration item to mask the check.

Check unused dependencies

The complement of the missing-dependency check above: Blade can also detect redundant dependencies — a library declared in deps whose public headers are never directly #included by the target's sources or headers. Redundant deps slow down builds, leak unnecessary transitive dependencies, and rot over time.

This check is on by default at 'warning' (advisory — reported but does not fail the build); via cc_config.unused_deps_severity set it to 'error' to fail the build, or 'debug' to silence. Advisory by default, like Bazel's unused_deps tool and Buck2.

When keep_deps is (and isn't) needed

keep_deps should be very rare in practice. Blade auto-exempts a number of structural patterns that legitimately show up as "unused" but are real dependencies, and a properly-structured BUILD usually doesn't need any keep_deps at all. Before adding a keep_deps entry, check whether one of the auto-exemptions below already covers your case, or whether the real fix is restructuring the dep — not silencing it.

Cases automatically exempted from the check

A dep is never reported as unused if any of these apply:

  1. The dep has no public headershdrs = [] (explicit) or, equivalently, the dep is configured as header-less. Auto-applied to:
  2. Libraries with an explicit hdrs = [] (no public interface).
  3. Libraries whose name is listed in cc_library_config.hdrs_missing_suppress. The user already declared "this library has no public header"; Blade treats it the same as hdrs = [].
  4. foreign_cc_library targets that don't enumerate explicit hdrs. The hdr_dir such a target registers is the in-source build-tree layout (e.g. thirdparty/gflags/gflags), but consumers #include from the installed layout (e.g. <gflags/gflags.h>), so the unused-deps check has no way to ever credit the dep as "used".

  5. The target has no scannable C/C++ source. If the target's srcs and hdrs between them contain zero files that Blade can scan for #include directives (umbrella libs with empty srcs, proto_library wrappers whose generated .cc lives under build_dir, generated-only or foreign builds, …), the check skips this target — there is no #include corpus to compare deps against, so every dep would be a false positive.

  6. Header re-export. When the umbrella target declares one of its own hdrs and at least one dep also declares the same header, the dep is treated as implicitly used. This is the umbrella-facade pattern (fiber:fiber listing async.h, future.h, … which fiber:async, fiber:future, … also own as their public headers).

  7. export_incs virtual paths. A library that ships its headers under a private subdirectory and exposes them via -Iexport_inc_path (e.g. protobuf-3.4.1 with src/google/protobuf/message.h + export_incs = ['src']) registers each header under both its full path and the consumer-visible relative path (google/protobuf/message.h), so #include <google/protobuf/message.h> resolves back to the protobuf target.

  8. System libraries (deps keyed #:NAME, e.g. #:dl, #:pthread). Their headers (<dlfcn.h>, <pthread.h>) do exist but Blade has no system-header → system-lib mapping, so a header-based check has nothing to consult.

  9. keep_deps and unused_deps_suppress — explicit user overrides.

When you genuinely need keep_deps (and when you don't)

After the auto-exemptions above, what's left is the "purely link-time" pattern: a dep whose symbols are reached at link time but not via any #include. Even here, keep_deps is usually NOT the right answer:

  • Self-registration libraries — protocol implementations, NSLBs, name resolvers, compressors, factory plugins that register themselves with a global registry via static initializers, and are never #included directly by consumers — should move their .h into srcs and declare hdrs = []. The library then auto-exempts via case (1) above. If a private header is incidentally needed by another target (e.g. its matching *_test.cc), Blade allows that consumer to #include it as long as the test depends on the library — private headers from a directly declared dep are reachable by name. This means you do not need to make the header public just so the test can include it.

  • Umbrella facades that re-export a sub-library's interface — list the sub-library's public header in your own hdrs. Case (3) covers this without keep_deps.

  • Transitive deps you re-list "just to be explicit" — drop them. If :hbase_channel already depends on :hbase_client_protocol, an umbrella :hbase_client that depends on :hbase_channel does not need a redundant direct dep on :hbase_client_protocol. The redundant dep is what triggers the "unused" notice in the first place.

A library depended on for its link-time side effects (e.g. self-registration via global objects) should declare link_all_symbols = True regardless, so the linker does not drop it.

keep_deps is appropriate only when none of the above applies — for example, an umbrella that bundles several link-only registrations none of whose headers it owns or re-exports.

cc_library(
    name = 'foo',
    srcs = ['foo.cc'],
    hdrs = ['foo.h'],
    deps = [':bar'],          # used via headers
    keep_deps = [':baz'],     # truly link-only, header-bearing, not auto-exempt
)

Common false-positive patterns (what looks like a redundant dep but isn't)

  • A private header declared as hdrs. A library whose .h is consumed only by its own .cc and matching *_test.cc does not need a public header at all. Move the .h into srcs, set hdrs = [], and case (1) auto-exempts the library for every consumer. The matching test can still #include the private header by name.
  • An umbrella facade not re-declaring the sub-library's hdrs. If the umbrella aggregates several sub-libraries, listing the sub-libraries' public headers in the umbrella's own hdrs lets case (3) auto-exempt the umbrella → sub-library deps.
  • A foreign_cc_library with an undeclared hdrs list. Without explicit hdrs, the hdr_dir is registered against the in-source layout and never matches consumer #includes; case (1) handles this automatically, but if you want missing-dep detection too, list the public headers explicitly under hdrs.
  • A library reached only through export_incs paths. Case (4) handles this — if you still see the check fire, verify the export_incs entry is the actual root (e.g. src, not . or the full path).
  • proto_library listed as a "link-only" dep. A proto_library's generated .pb.h is currently not unified with _hdr_targets_map in every code path; it may need to remain in keep_deps for now. (Fix tracked.)

Tip: don't reach for keep_deps or unused_deps_suppress to silence the check until you've ruled out the structural fixes above. The patterns that look like "Blade is being annoying" are almost always actual structural smells (a private detail leaking through hdrs, an umbrella that doesn't re-export, a redundant transitive dep) that the check is correctly surfacing.

Deps listed in cc_config.unused_deps_suppress as a {target: [deps]} map are also exempt (mainly for incrementally cleaning up an existing code base, where editing every BUILD file at once is impractical).

Static undefined-symbol check

Blade's missing-deps check (above) catches header omissions. The companion undefined-symbol check catches the link-time complement: a cc_library references a symbol that no declared dep — including transitive cc_library deps and #alias system libraries — actually defines.

Without this check, a missing deps entry surfaces only at the final binary's link step, often as a wall of undefined reference to ... errors that name the binary, not the broken library. The check moves that failure left in time: per-library, right after the archive is produced, with an error message that names the specific missing dep.

How it works:

  • nm -u <archive> enumerates undefined symbols on the target's just-built .a.
  • nm --defined-only does the same for each transitive cc_library dep's archive.
  • Each #alias system library (#m, #pthread, #dl, …) declared in the transitive deps contributes a pre-generated symbol cache.
  • An internal baseline absorbs platform symbols (libc, libstdc++, weak refs).
  • Anything still unresolved is reported with the symbol and the target where it appears, and fails the build.

Where it runs. On Linux and macOS. Skipped on MSVC because link.exe already rejects undefined externals via LNK2019, and Microsoft's .obj DEFAULTLIB directives resolve standard C/C++ symbols outside the source-visible -l<name> graph in ways the nm model can't faithfully represent. Also skipped per-target when check_undefined = False or allow_undefined = True.

When generate_dynamic is True. The check still runs. The final shared link's -Wl,--no-undefined is the authoritative answer, but nm catches the same misses per-library, before any link runs, which is faster and points at the specific dep that needs to change.

Controlling the check.

Legitimate undefined-symbol cases. A plugin loaded into a host process needs symbols that only the host's binary provides; setting allow_undefined = True opts that library out of both the static check and the link-time --no-undefined. For a narrower exception — say a single symbol the toolchain emits but doesn't link — use the list form allow_undefined = [r'__some_symbol'] so the rest of the closure is still enforced.

Cost vs. --generate-dynamic. Before this check existed, the usual way to validate that every cc_library's deps were complete was to build the whole project with --generate-dynamic: each shared link runs through -Wl,--no-undefined, which fails the link on a missing dep. That works, but it has to (a) actually link every shared library, even when no dynamic linkage is otherwise wanted, and (b) only fails at link time, so the error message names the binary rather than the offending library. The static check moves the same validation to archive time and per-library.

Measured on a real codebase (Tencent/flare's flare/rpc/..., 180 cc targets, macOS arm64, Python 3.14, -j10):

Scenario Wall-clock Notes
Cold full build, --generate-dynamic --no-cc-check-undefined 2m56s static + dynamic, no validation
Cold full build, --generate-dynamic --cc-check-undefined 3m06s static + dynamic + static check
Static-check overhead on a cold build +10s (+5.7%) overlaps with link work
Warm pure-check phase (incremental rerun) 4.2s 180 targets, sources cached
Warm dylib-relink phase, --no-cc-check-undefined 3.1s 91 dylibs, sources cached

In other words: on a project that doesn't actually need dynamic libraries, --cc-check-undefined is the cheap way to validate the dep graph — a few seconds on top of a normal static build — whereas --generate-dynamic makes you pay the full cost of every shared link just to find a missing dep. They are complementary, not redundant: keep using --generate-dynamic if you want shared libraries; use the static check for graph-completeness alone.

prebuilt_cc_library

For libraries without source code, library should be put under the lib{32,64} sub dir accordingly. The attributes deps, export_incs, link_all_symbols is still avialiable, but other attributes, include compile and link related options, are not not present in prebuilt_cc_library.

The library files can be located in two ways:

  • By name convention (default): the file is lib<name><suffix> under a lib{32,64}-style subdirectory.
  • By explicit path: give the paths directly with static_library / dynamic_library (below) — useful when the file names or layout don't follow the convention.

Attributes:

  • libpath_pattern: str, The subdirectory which contains the library files. It default to cc_library_config.prebuilt_libpath_pattern config. See cc_library_config.prebuilt_libpath_pattern for more details.
  • static_library: str, explicit path (relative to the target's dir) to the static archive (.a / .lib).
  • dynamic_library: str, explicit path (relative to the target's dir) to the shared library (.so / .dylib / .dll).
  • import_library: str, explicit path (relative to the target's dir) to a Windows import library (.lib) — the file you link against to use a .dll. Windows-only: it takes effect on the MSVC toolchain and is ignored elsewhere. When set, blade links the import lib for dynamic linking and treats dynamic_library (the .dll) as a runtime-only artifact (flattened into the test/run runfiles). On MSVC, setting dynamic_library requires import_library — a .dll cannot be linked directly, and you can't tell an import .lib from a true static .lib by name (so static_library vs import_library disambiguates). When any explicit path is set, at least one is required, the name convention is skipped, and libpath_pattern is ignored (a warning is emitted). As with convention mode, when only one kind is present it serves both static and dynamic linking.

Example:

# By name convention (looks for lib<name> under the lib{32,64} subdir):
prebuilt_cc_library(
    name = 'mysql',
    deps = [':mystring', '#pthread']
)

# By explicit path:
prebuilt_cc_library(
    name = 'foo',
    hdrs = ['foo.h'],
    static_library = 'lib/libfoo.a',
    dynamic_library = 'lib/libfoo.so',
    export_incs = ['include'],
)

# Windows: link the import lib, ship the DLL as the runtime payload:
prebuilt_cc_library(
    name = 'bar',
    hdrs = ['bar.h'],
    import_library = 'lib/bar.lib',
    dynamic_library = 'bin/bar.dll',
    export_incs = ['include'],
)

foreign_cc_library

NOTE: This feature is still experimental.

There are already a large number of existing libraries in the world. They are built with different build systems. If you want to add Blade build support to them, you need to invest a lot of time cost and maintenance. foreign_cc_library is used to describe C/C++ libraries not built directly by Blade but generated by other build systems, such as make or cmake. The main difference between foreign_cc_library and prebuilt_cc_library is that the library it describes is dynamically generated by Blade during the build by calling other build systems, The library described by prebuilt_cc_library is placed in the source tree before the build. So generally foreign_cc_library always needs to be used with gen_rule.

Considering that a large number of C/C++ libraries are built with GNU Autotools, the default parameters of foreign_cc_library are adapted to the autotools installation directory layout. In order to find the library and header files correctly, foreign_cc_library assumes that the package will be installed in a certain directory after the build (that is, the path specified by the --prefix parameter of configure), and the header file is in the include subdirectory, The library file is installed in the lib subdirectory.

Attributes:

  • name: str, The name of the library
  • install_dir: str, The installation directory after the package is built
  • lib_dir: list, The subdirectory name of the library in the installation directory
  • has_dynamic: bool = False, Whether this library has a dynamic linked edition.
  • static_library / dynamic_library / import_library: str, explicit paths (relative to install_dir) to the libraries the foreign build produces, overriding the lib_dir/has_dynamic name convention. Useful when the build emits names the lib<name>.<suffix> convention can't express (version suffixes, several libs, etc.). import_library is the Windows import .lib for a produced .dll (required on MSVC when a dynamic_library is given). Same two-mode behaviour as prebuilt_cc_library; the link selection, runfiles, and check_undefined .syms handling are shared between the two rules.

Example1, zlib

zlib can be the simplest example of autotools package. Assuming that zlib-1.2.11.tar.gz is in the thirparty/zlib directory, its BUILD file is thirdparty/zlib/BUILD:

# Assume that after executing this rule, the built package will be installed under `build_release/thirdparty/openssl`,
# then the header file is under `include/openssl`, and the library file is under `lib`.
# We have developed general build rules for autotools and cmake, but they are still in the experimental state.
# Here we still use `gen_rule` to examplify.
gen_rule(
    name = 'zlib_build',
    srcs = ['zlib-1.2.11.tar.gz'],
    outs = ['lib/libz.a', 'include/zlib.h', 'include/zconf.h'],
    cmd = '...',  # tar xf, configure, make, make install...
)

# Describe the installed library of zlib

foreign_cc_library(
    name = 'z',  # The name of the library is `libz.a`, under the `lib` subdirectory
    install_dir = '', # The installation directory is `build_release/thirdparty/libz`
    # lib_dir= 'lib', # The dafault os OK and can be omitted.
    deps = [':zlib_build'],
)

Use the above library:

cc_binary(
    name = 'use_zlib',
    srcs = ['use_zlib.cc'],
    deps = ['//thirdparty/openssl:ssl'],
)

use_openssl.cc:

#include "thirdparty/zlib/include/zlib.h"
// or
#include "zlib.h"
// because `thirdparty/zlib/include` has been exported in the `foreign_cc_library` defaultly

Example 2, openssl

Strictly speaking, openssl is not built with autotools, but it is generally compatible with autotools. Its autotools-like configure file is Config, and the directory layout after installation is compatible. However, its header file paths have package name prefix, which is not directly under include but under the include/openssl subdirectory.

Suppose openssl-1.1.0.tar.gz is in the thirparty/openssl directory, and its BUILD file is thirdparty/openssl/BUILD:

# Assuming that after executing this rule, the built package will be installed under `build_release/thirdparty/openssl`,
# then the header file is under `include/openssl` and the library file is under `lib`.
gen_rule(
    name = 'openssl_build',
    srcs = ['openssl-1.1.0.tar.gz'],
    outs = ['lib/libcrypto.a', 'lib/libss.a'],
    cmd = '...',  # tar xf, Config, make, make install...
    export_incs = 'include', # Let the compiler find the `openssl` subdirectory under `include`
)

# Describe the above 2 libraries

foreign_cc_library(
    name = 'crypto',  # The name of the library is libcrypto.a, under the `lib` subdirectory
    install_dir = '', # The installation directory is `build_release/thirdparty/openssl`
    deps = [':openssl_build'],
)

foreign_cc_library(
    name = 'ssl',  # The name of the library is libssl.a, under the `lib` subdirectory
    install_dir = '',
    deps  = [':openssl_build', ':crypto'],
)

Use the above library:

cc_binary(
    name = 'use_openssl',
    srcs = ['use_openssl.cc'],
    deps = ['//thirdparty/openssl:ssl'],
)

use_openssl.cc:

#include "openssl/ssl.h"  // Contains package name

cc_binary

Build executable from source.

Example:

cc_binary(
    name='prstr',
    srcs=['./src/mystr_main/mystring.cpp'],
    deps=['#pthread',':lowercase',':uppercase','#dl'],
)
  • dynamic_link:bool = False

cc_binary is static linked defaultly for easy deploying, include libstdc++. For some technology limitation, glibc is not static linked. we can link it statically, but there are still some problems.

If you want to generate a dynamic linked executable, you can set it to True,all dependency libraries will be linked statically, except prebuilt libraries if they has no dynamic version. This may reduce disk usage, but program will startup slower. Usually it can be use for local testing, but it didn't fit for deploy.

  • export_dynamic: = True

Usually, dynamic library will only access symbols from its dependencies. but for some special case, shared library need to access symbols defined in the host executable, so come the attribute.

This attribute tells linker to put all symbols into its dynamic symbol table. make them visible for loaded shared libraries. for more details, see --export-dynamic in man ld(1).

export_dynamic is an ELF (GNU-ld / ld64) feature. It has no effect on MSVC (Windows has no "export all" mode; the option is ignored with a warning). On Windows, export symbols explicitly with export_map (below).

  • export_map (on cc_binary): the same export-control file used by cc_library also lets an executable export selected symbols, so a LoadLibrary'd plugin DLL can resolve them back into the host (issue #1201). On GNU-ld / ld64 it is the link-time version script as usual; on MSVC Blade turns it into a COMDAT-filtered .def, passes /DEF so the exe exports exactly the listed symbols, and link.exe additionally emits an import library <name>.lib that a plugin DLL links (e.g. via a prebuilt_cc_library) to call back into the host. Export is always explicit — Windows favors a curated set over exporting everything (which would blow the PE 64K export ceiling and expose internals); for a one-off symbol, linkflags = ['/EXPORT:<symbol>'] works without an export map.
cc_binary(name = 'host', srcs = ['host.cc'], export_map = 'host_api.map')
  • strip: bool = False

Strip the executable after linking to reduce its size (symbols/debug info are removed). Blade links to a <name>.unstripped sidecar and runs strip into the final output. Only supported on the GNU toolchain (gcc); skipped with a notice elsewhere. Compatible with DebugFission — the .dwp is packaged from the objects, so you can ship a stripped binary alongside a separate .dwp for debugging.

  • strip_options: string[] = []

Options passed to strip when strip = True. Defaults to ['--strip-unneeded']. Use ['--strip-all'] (or ['-s']) for the smallest binary, ['--strip-debug'] to drop only debug info, or ['-K', '<symbol>'] to keep specific symbols. See man strip(1).

  • extra_linkflags: string[] = []

Extra flags passed to the linker for this target (see the common attribute above), e.g. extra_linkflags=['-Wl,-z,now'].

Using dwp files

When DebugFission is enabled (via cc_config.fission) and dwp packaging is enabled (via cc_config.dwp), the debug information is split into separate .dwo files, which are then packaged into a single .dwp file for each binary target.

If you need to deploy the binary with debug information for debugging in production or other environments, you can include the dwp file in your package by referencing it through the binary target:

package(
    name = 'server_package',
    ...,
    srcs = [
        # executable
        ('$(location //server:server)', 'bin/server'),
        # Include the dwp file for server binary
        ('$(location //server:server dwp)', 'bin/server.dwp'),
        ...,
    ],
)

The $(location :target dwp) syntax allows you to reference the dwp file generated for a specific binary target.

cc_test

cc_binary, with gtest gtest_main be linked automatically,

Test will be ran in a test sandbox, it can't access source code directly. If you want to pass some data files into it, you must need testdata attribute.

  • testdata: list = []

Copy test data files into the test sandbox, make them visible to the test code. This attribute supports the following forms:

  • 'file'

    Use the original filename in test code. - '//your_proj/path/file'

    Use "your_proj/path/file" form to access in test code. - ('//your_proj/path/file', "new_name")

    Use the "new_name" in test code.

Example:

cc_test(
    name = 'textfile_test',
    srcs = 'textfile_test.cpp',
    deps = ':io',
    testdata = [
        'test_dos.txt',
        '//your_proj/path/file',
        ('//your_proj/path/file', 'new_name')
    ]
)

cc_plugin

Link all denendencies into the generated so file, make it can be easily loaded in other languages.

cc_plugin(
    name='mystring',
    srcs=['./src/mystr/mystring.cpp'],
    deps=['#pthread',':lowercase',':uppercase','#dl'],
    warning='no',
    defs=['_MT'],
    optimize=['O3']
)

Attributes:

  • prefix: str | None = None, the file name prefix of the generated dynamic library. The default None means "use the current toolchain's platform default" (lib on Linux/macOS, empty on Windows).
  • suffix: str | None = None, the file name suffix of the generated dynamic library. The default None means "use the current toolchain's platform default" (.so on Linux, .dylib on macOS, .dll on Windows).
  • allow_undefined: bool = False, whether undefined symbols are allowed when linking. Because many plug-in libraries depend on the symbol names provided by the host process at runtime, the definition of these symbols does not exist in the link phase.
  • strip: bool = False, whether to remove the debugging symbol information. If enabled, the size of the generated library can be reduced, but symbolic debugging cannot be performed. Only supported on the GNU toolchain (gcc); skipped with a notice elsewhere.
  • strip_options: string[] = [], options passed to strip when strip = True. Defaults to ['--strip-unneeded'] (safe for a shared object). Use e.g. ['--strip-debug'] to drop only debug info, or ['-K', '<symbol>'] to keep specific symbols. See man strip(1).
  • linker_script: str, a single linker script used to control the linking process. Its role is mainly to specify how to put the sections in the input file into the output file and to control the layout of the sections in the input file in the program address space. The linker has a default built-in linking script, which can be viewed with ld --verbose. This option will replace the system's default linking script. The linker script file usually has the extension .ld or .lds. Only a single file is accepted: a SECTIONS script replaces the default, so multiple -T scripts do not meaningfully combine. Linker scripts are usually quite complex, so if you just want to control the visibility of the symbols, use the export_map option below.

    GNU-ld feature. linker_script is the GNU-ld -T option. It works with a GNU-ld-compatible linker: GNU ld / ld.lld on ELF (Linux), and MinGW's GNU ld when targeting Windows. It is not supported by MSVC link.exe or Apple's ld64: on macOS Blade emits a warning and drops the script (previously the link would fail with a cryptic "unknown options" error); on MSVC this attribute doesn't reach _cc_link. The plural linker_scripts (a list) is a deprecated alias; using it emits a warning and only the first file is used.

  • export_map: str, a single linker "version" script used to control which symbols the shared library exports. Despite the linker term "version script", the mechanism here is export filtering, not ABI versioning — hence the name export_map (the industry term for a symbol-export control file). Use the anonymous-version form (no version id) to control visibility only. Only a single file is accepted (GNU ld rejects more than one anonymous version node). It is available on cc_library, cc_binary and cc_plugin. It is passed to GNU ld's --version-script; the export-map files usually have the extension .exp, .sym, .ver or .map. See the C++ example below.

    Cross-platform. On Linux it is passed straight to GNU ld. On MSVC there is no --version-script, so Blade applies the map itself: it filters the auto-generated .def (see Windows DLL support) by matching each symbol's demangled name (UnDecorateSymbolName) against the global/local patterns. macOS (Apple ld64) is handled the same way: Blade enumerates each .o's globals via nm, demangles each via libc++abi's __cxa_demangle, matches against the script, and writes the matching (Mach-O-underscore-prefixed) mangled names into ld64's -exported_symbols_list file. Because demangling is name-only, two limitations apply on both MSVC and macOS: overloads collapse (a name is exported with all its overloads or none), and a quoted signature pattern ("f(int)") is matched by its name part only (with a one-time warning). Full ELF symbol versioning (version nodes) is Linux-only. In practice, writing patterns in the ns::class::method form is sufficient. linker_script is likewise a GNU-ld option (see its note above). The plural version_scripts (a list) is a deprecated alias for export_map; using it emits a warning and only the first file is used.

prefix and suffix control the file name of the generated dynamic library. Assuming name='file', on a Linux toolchain the default output is libfile.so; setting prefix='' makes it file.so. Passing a name that already carries a shared-library extension (e.g. name='file.so') is no longer implicitly treated as "the full output file name"; to fully customize the output name, pass prefix='' and suffix='.so' explicitly.

Controlling which symbols a shared library exports

To control the external visibility of symbols in the linking result, you can use GCC extended attributes in the source code or command line options. When you don't want to (or can't) annotate the sources, export_map does the same from the link line: it lists the symbols to keep global (exported); everything under local becomes hidden.

A C++ example

Suppose a library should expose only the mylib::Api class and the mylib::Create() factory, and hide everything else (helpers, third-party symbols pulled in statically, etc.):

// api.h
namespace mylib {
class Api {
 public:
    void Run();
};
Api* Create();
}  // namespace mylib

Because C++ symbols are mangled, list the demangled names inside an extern "C++" block. Write the export_map file (e.g. api.map):

{
global:
    extern "C++" {
        # Quoted names are matched literally (no wildcard) -- use this when
        # the exact signature matters or to avoid matching overloads.
        "mylib::Create()";

        # Unquoted names allow wildcards: export every member of mylib::Api
        # (constructors, Run(), ...).
        mylib::Api::*;

        # vtable / typeinfo are needed by code that inherits from or
        # dynamic_casts the class across the library boundary. The demangler
        # prints a space here; match it with the '?' single-char wildcard.
        typeinfo?for?mylib::Api;
        vtable?for?mylib::Api;
    };
local:
    *;  # hide everything not listed above
};

Wire it into the BUILD file (only the shared library is affected; the static archive is unchanged):

cc_library(
    name = 'mylib',
    srcs = ['api.cc'],
    hdrs = ['api.h'],
    export_map = 'api.map',
)

To see the visibility of symbols in the library, you can use the nm command.

000000000000010c t _init
                 U puts@@GLIBC_2.2.5
00000000000000000060 t register_tm_clones
00000000000000f0 T hello
00000000000000000100 t world

The second column is the symbol type. If lowercase, the symbol is usually local; if uppercase, the symbol is global (external). U is the undefined external symbols that the library depends on, don't care.

cc_plugin is mainly used to create various extensions, such as JNI, python extension and other dynamic libraries that are dynamically loaded by calling certain functions during runtime. It will be ignored when linking even if it appears in the deps of other cc targets.

Windows DLL support

On a Windows (MSVC) toolchain, a cc_library that needs a shared library is built as a DLL plus an import library, mirroring the .so it would produce on Linux. This happens under the same conditions as on other platforms: the library is depended on by a dynamic_link executable, or you build with --generate-dynamic.

Two Windows specifics are handled for you:

  • Automatic export. Unlike ELF, where every global symbol is exported by default, Windows exports nothing unless symbols are marked __declspec(dllexport) or listed in a module-definition (.def) file. To avoid forcing source changes, Blade scans the library's object files and generates a .def that exports every defined, externally-visible symbol — except symbols in deduplicated COMDAT sections (template instantiations, inline functions, and similar). C++ vtable and RTTI symbols are an exception: they are kept even though they are COMDAT, so a polymorphic class consumed via __declspec(dllimport) links correctly. This makes a plain cc_library usable as a DLL with no annotations, the way it already is as a .so. The same objects serve both static and shared linking, so you never write dllexport and the library is never compiled twice.
  • Runtime discovery. Windows has no rpath, and a PE import table records only a DLL's base name. So at test/run time Blade flattens every dependency DLL into the target's runfiles directory and prepends that directory to PATH — the Windows analog of LD_LIBRARY_PATH. Because each DLL's name encodes its package path, flattening never collides.

Use export_map to narrow the export set when you want export discipline or to stay under the PE 64K export limit.

Consumer-side limitations

Exporting is automated, but importing is not symmetric on Windows — the consumer side still follows the platform ABI. When you #include a header from a Blade-built DLL and use its symbols:

  • Functions — usable with no annotation; the import library provides a thunk.
  • Global variables / static data members — the consumer must declare them __declspec(dllimport); without it the compiler reads the import-table slot (a pointer) instead of the variable's value, silently producing wrong results. A library that relies on shared global mutable state — most notably a test framework with a global test registry — should instead set generate_dynamic = False so it is linked statically into the executable. With that, the rest of the dependency graph can still be DLLs while the stateful library stays correct.
  • Polymorphic classes — Blade exports the vtable and RTTI, so a class consumed via __declspec(dllimport) links. You still need __declspec(dllimport) on the class declaration (the compiler references the imported vtable/RTTI).
  • Implicit special members — a dllimport class also imports its constructor / destructor / assignment. Auto-export only exports symbols actually emitted in the DLL, so an implicit member used only by the consumer is never emitted there and fails to link. Declare such members out-of-line (e.g. Greeter::Greeter() = default; in a .cc) to force the DLL to emit and export them, or expose a factory function so the consumer never constructs the class directly.
  • Consistency — whether you compile a consumer for static or DLL use must match the library you actually link. A mismatch produces LNK2019 (unresolved) or LNK4217 (locally defined symbol imported) errors.

On Linux/macOS none of this applies: imports resolve through the GOT with no annotation, so a header written for those platforms needs no decoration on the use side.

Recommended: prefer a function-based ABI (accessors / factory functions) across DLL boundaries rather than exporting global variables or polymorphic classes directly. Functions need no dllimport, share one set of objects across static/dynamic, and keep headers free of platform-specific decoration.

Automating the consumer-side import macro (so headers need no manual dllimport) is planned but not yet implemented; for now legacy exported-class headers must keep their own import macro.

windows_resources

Compile Windows resource (.rc) files into .res object files using the Windows SDK Resource Compiler (rc.exe). The resulting .res files are automatically linked into any cc_binary that depends on this target via deps.

On non-Windows platforms this rule is a no-op: it resolves to nothing and has no effect on the build.

windows_resources(
    name = 'hello_res',
    rc_files = ['hello_gui.rc', 'version.rc'],
    hdrs = ['resource.h'],
    resources = ['image/blade.ico'],
)

Attributes:

  • rc_files (required): string[] — Resource script files (.rc) to compile.
  • hdrs (optional): string[] — Header files included by the .rc scripts.
  • resources (optional): string[] — Binary resource files (e.g., .ico, .bmp) referenced by the .rc scripts. Changes to these files trigger a rebuild.

The .res files produced by this target appear as inputs to the linker command, just like object and library files. Use it with cc_binary:

cc_binary(
    name = 'hello_gui',
    srcs = ['hello_gui.c'],
    extra_linkflags = ['/SUBSYSTEM:WINDOWS', 'user32.lib'],
    deps = [':hello_res'],
)

resource_library

Compile static data file to be resource, which can be accessed in the program directly.

When we deploy a program, it often requires many other files to be deployed together. it's often boring. Blade support resource_library, make it quite easy to put resource files into the executable easily. For example, put there static pages into a library:

resource_library(
    name = 'static_resource',
    srcs = [
        'static/favicon.ico',
        'static/forms.html',
        'static/forms.js',
        'static/jquery-1.4.2.min.js',
        'static/jquery.json-2.2.min.js',
        'static/methods.html',
        'static/poppy.html'
    ]
)

It will generate a library libstatic_resource.a,and a header file static_resource.h(with full include path).

When you need tp use it, you need to include static_resource.h(with full path from workspace root) and also "common/base/static_resource.h",

Use STATIC_RESOURCE macro to simplify access to the data (defined in "common/base/static_resource.h"):

StringPiece data = STATIC_RESOURCE(poppy_static_favicon_ico);

The argument of STATIC_RESOURCE is the full path of the data file, and replace all of the non-alphanum and hyphen character to unserscore(_):

The data is readonly static storage. can be accessed in any time.

NOTE: There is a little drawback for static resource, it can;t be updated at the runtime, so consider it before using.

cu_library

build a C++ library target containing CUDA device code using nvcc.

Rules are similar to cc_library .

The attributes cuda_path and extra_cuflags are added.
cuda_path is pointed to cuda installed path in the local repository with prefix //, with that nvcc binary {cuda_path}/bin/nvcc and include search path {cuda_path}/include can be found automatically. Environment variable below will be ignored if use cuda_path.
extra_cuflags support some flags which only work for cuda.

Environment variable NVCC is pointed to the path of nvcc binary,such as NVCC=/usr/local/cuda/bin/nvcc blade build.
Environment variable CUDA_PATH is pointed to the installed path of cuda, with which {CUDA_PATH}/include and {CUDA_PATH}/samples/common/inc will be added to include search path.

Priority: cuda_path > NVCC/CUDA_PATH.

Example:

cu_library(
    name = 'template_gpu',
    srcs = ['template_gpu.cu'],
    hdrs = [],
    # cuda_path = '//thirdparty/cuda',
)

cu_binary

build a C++ executable target containing CUDA device code using nvcc.

Rules are similar to cc_binary . Use environment variables reference to cu_library.

Example:

cu_binary(
    name = 'template',
    srcs = ['template.cu'],
    deps = [':template_cpu'],
    # cuda_path = '//thirdparty/cuda',
)

cu_test

build a C++ ut target containing CUDA device code using nvcc.

Rules are similar to cc_test . Use environment variables reference to cu_library.

Example:

cu_test(
    name = 'cu_test',
    srcs = ['cu_test.cu'],
    deps = [':template_cpu'],
    # cuda_path = '//thirdparty/cuda',
)