Assuming you have two classes A and B as outlined below that are valid C++, and you are having trouble setting up build rules. You have a few options for modifying either you build rules or refactoring your code.
class_a.h
#ifndef class_a_h__
#define class_a_h__
// Forward declare class A
class A;
// Include dependencies here in case they require class A.
#include "class_b.h"
// Actually declare class A
class A {
void SetB(B* b);
// other fields and methods that may use b
};
#endif // class_a_h__
class_b.h
#ifndef class_b_h__
#define class_b_h__
// Forward declare class B
class B;
// Include dependencies here in case they require class B
#include "class_a.h"
// Actually declare class B
class B {
public:
void SetA(A* a);
// other fields and methods that may use a
};
#endif // class_b_h__
Option 1: Declare libA and libB without declaring their interdependency.
The magic here is including the other class header in the dependent class lib's srcs. You can make one depend on the other but not in both directions.
Pros:
- No need to refactor the source.
- Minimal work to refactor the BUILD file so good for temporary debugging aids.
Cons:
- Bazel won't know why the linker can't find missing symbols if some_other_lib doesn't depend on both lib_a and lib_b.
BUILD.bzl
cc_library(name="lib_a",srcs=["class_a.cc", "class_b.h"],hdrs=["class_a.h"])
cc_library(name="lib_b",srcs=["class_b.cc", "class_a.h"],hdrs=["class_b.h"])
cc_library(name="some_other_lib",srcs=["other.cc"],hdrs=["other.h"],deps=[":lib_a",":lib_b"])
Option 2: Create one library that holds both class A and class B.
I have mixed feelings about this one, but it's probably what I would suggest by default. Because of the cyclic dependency you should never be using one lib without the other so I think it's ok to combine them into a single target.
Pros:
- Accurately captures dependency graph.
- No need to refactor the source.
Cons:
- Less granular dependency graph. i.e. You can not depend on just one of the libs.
- If your setup is complicated enough this could turn a Makefile with obvious dependencies into a single cc_library of all your code.
BUILD.bzl
cc_library(name="lib_a_and_b",srcs=["class_a.cc", "class_b.cc"],hdrs=["class_a.h", "class_b.h"])
cc_library(name="some_other_library",srcs=["other.cc"],hdrs=["other.h"],deps=[":lib_a_and_b"])
Option 3: Extract base class(es).
This can be an option, but is just always complicated for any real world scenario. Do you make class C that takes A and B as parameters and then make usages of A and B use C instead or some other permutation depending on how you need them to work. I'm pretty sure this just isn't possible for something like an event bus that knows about event handlers and the handlers need to know about the bus to send other events.
Pros:
- Possibly results in better code.
- No build "magic".
Cons:
- Not trivial.
- Not always possible.
- Might result in worse code.
- Requires modifying the source code.
Option 3a: Combine class A and class B into a single class.
If this is an option for you, then it is a more simple refactor option. The classes are obviously already related. Maybe they make more sense as a single class.
Pros:
- Trivial refactor
- No build "magic"
Cons:
- Not always possible.
- Might violate your team's coding principles.
- Requires modifying the source code.