fietsje: implement .replace, add Cilium dependencies.

The new .replace() can be used to mirror 'replace' stanzas in go.mod,
and that's what it's being used for in Cilium, as it ships a handful of
forked libraries that we have to pull in.

The Cilium targets are currently unused, but the ones confirmed to build
are:

 - @com_github_cilium_cilium//cilium: cilium API client
 - @com_github_cilium_cilium//daemon:daemon: cilium daemon/agent
 - @com_github_cilium_cilium//operator: cilium operator

These currently built as dynamic libraries - turning them into
static/pure builds will come in a later build.

Test Plan: how do we test this? :)

X-Origin-Diff: phab/D542
GitOrigin-RevId: b38c7c1d0be8b0b88ea8f6992c9c5557189399cc
diff --git a/build/fietsje/planner.go b/build/fietsje/planner.go
index 4e67c2d..3aaefef 100644
--- a/build/fietsje/planner.go
+++ b/build/fietsje/planner.go
@@ -121,6 +121,24 @@
 	return c.with().use(paths...)
 }
 
+// replace injects a new dependency with a replacement importpath. This is used to reflect 'replace' stanzas in go.mod
+// files of third-party dependencies. This is not done automatically by Fietsje, as a replacement is global to the
+// entire build tree, and should be done knowingly and explicitly by configuration. The 'oldpath' importpath will be
+// visible to the build system, but will be backed at 'newpath' locked at 'version'.
+func (c *collection) replace(oldpath, newpath, version string) *collection {
+	// Ensure oldpath is in use. We want as little replacements as possible, and if it's not being used by anything,
+	// it means that we likely don't need it.
+	c.use(oldpath)
+
+	d := c.highlevel.child(oldpath, version)
+	d.replace = newpath
+	c.transitive[oldpath] = d
+	c.p.available[oldpath] = d
+	c.p.enabled[oldpath] = true
+
+	return c
+}
+
 // inject adds a dependency to a collection as if requested by the high-level dependency of the collection. This should
 // be used sparingly, for instance when high-level dependencies contain bazel code that uses some external workspaces
 // from Go modules, and those workspaces are not defined in parsed transitive dependency definitions like go.mod/sum.
@@ -170,6 +188,16 @@
 	}
 }
 
+func forceBazelGeneration(d *dependency) {
+	d.forceBazelGeneration = true
+}
+
+func buildExtraArgs(args ...string) buildOpt {
+	return func(d *dependency) {
+		d.buildExtraArgs = args
+	}
+}
+
 // use enables given dependencies defined in the collection by a high-level dependency, with any set build options.
 // After returning, the builder degrades to a collection - ie, all build options are reset.
 func (o *optionized) use(paths ...string) *collection {