Add dependencies to targets

This adds a dependencies field to the manifest target schema, listing
other targets on which one depends.

Change-Id: Ib9671761234539a0d10845c9fbeb88db8bbaf0c6
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/qg/+/124954
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Alexei Frolov <frolv@google.com>
diff --git a/qg/src/project/manifest.rs b/qg/src/project/manifest.rs
index 5e6e003..eef95a4 100644
--- a/qg/src/project/manifest.rs
+++ b/qg/src/project/manifest.rs
@@ -80,8 +80,14 @@
 /// A buildable target.
 #[derive(Debug, Deserialize, Serialize)]
 pub struct Target {
+    /// Provider-specific metadata about how to build the target. If this is
+    /// `None`, the target is virtual, grouping only other target dependencies.
     #[serde(flatten)]
     pub desc: Option<TargetType>,
+
+    /// List of other targets on which this one depends.
+    #[serde(default)]
+    pub deps: Vec<String>,
 }
 
 #[derive(Debug, Deserialize, Serialize)]
diff --git a/qg/src/registry.rs b/qg/src/registry.rs
index dbad628..2ed229f 100644
--- a/qg/src/registry.rs
+++ b/qg/src/registry.rs
@@ -12,7 +12,10 @@
 // License for the specific language governing permissions and limitations under
 // the License.
 
-use std::{collections::HashMap, sync::Arc};
+use std::{
+    collections::{HashMap, HashSet},
+    sync::Arc,
+};
 
 use crate::target::{Provider, Target};
 
@@ -24,6 +27,9 @@
 
     /// All known package providers by name.
     providers: HashMap<String, Provider>,
+
+    /// Unknown target dependencies.
+    unresolved_dependencies: HashSet<String>,
 }
 
 impl Registry {
@@ -33,6 +39,7 @@
         Self {
             targets: HashMap::new(),
             providers: HashMap::new(),
+            unresolved_dependencies: HashSet::new(),
         }
     }
 
@@ -55,18 +62,28 @@
             .is_none()
     }
 
-    pub(crate) fn add_target(&mut self, package: Target) -> bool {
-        let Some(provider) = self.providers.get(package.provider()) else {
+    /// Inserts a target into the registry.
+    pub(crate) fn add_target(&mut self, target: Target) -> bool {
+        let Some(provider) = self.providers.get(target.provider()) else {
             return false;
         };
 
         let slug = if provider.global {
-            package.name().to_string()
+            target.name().to_string()
         } else {
-            format!("{}:{}", provider.name, package.name())
+            format!("{}:{}", provider.name, target.name())
         };
 
-        let target = Arc::new(package);
+        for dep in target
+            .dependencies()
+            .filter(|&d| !self.targets.contains_key(d))
+        {
+            self.unresolved_dependencies.insert(dep.into());
+        }
+
+        self.unresolved_dependencies.remove(&slug);
+
+        let target = Arc::new(target);
         self.targets.insert(slug, target);
 
         true
diff --git a/qg/src/target.rs b/qg/src/target.rs
index a72a877..dfb6bc5 100644
--- a/qg/src/target.rs
+++ b/qg/src/target.rs
@@ -41,11 +41,21 @@
     }
 }
 
-/// An installable `qg` package.
+/// An buildable `qg` target.
 #[derive(Debug)]
 pub struct Target {
+    /// The target's name, without a provider namespace.
     name: String,
+
+    /// The name of the target's provider.
     provider: String,
+
+    /// List of targets on which this one depends, storing their full namespaced
+    /// paths.
+    dependencies: Vec<String>,
+
+    /// Information about how to build the target, specific to a type of
+    /// provider.
     metadata: Metadata,
 }
 
@@ -70,6 +80,7 @@
         Self {
             name: name.to_owned(),
             provider: provider.to_owned(),
+            dependencies: target.deps,
             metadata: target.desc.map_or(Metadata::DepOnly, Metadata::from),
         }
     }
@@ -86,6 +97,11 @@
         &self.provider
     }
 
+    /// Returns the package's dependencies.
+    pub fn dependencies(&self) -> impl Iterator<Item = &str> {
+        self.dependencies.iter().map(String::as_str)
+    }
+
     /// Returns the type of the package as a string.
     #[must_use]
     pub fn type_string(&self) -> &str {