Every Bazel rule (e.g. cxx_library) has attributes and an internal implementation function in Starlark. The implementation can read the attributes and coordinates the work of actually calling programs (like Clang) and collecting the outputs. The collected outputs are exposed via a list of data-structures of type "Provider". When a target depends on other targets, it can see the providers of its direct dependencies. This allows targets to build on their dependencies. In C/C++, for example, the Providers contain the header files, the objects for linking etc.
I would definitely write (or at least read) a few Bazel rules before writing a new build system that improves on it!