Skip to content

Commit 2c67e2a

Browse files
Try avoiding a full clean in cargo sqlx prepare --merged (#1802)
* refactor(sqlx-cli): Try avoiding a full clean with `--merged` * docs(sqlx-cli): Sprinkle some comments on the metadata changes * refactor(sqlx-cli): Make the new recompiltion setup unit-testable * fix(sqlx-cli): Only pass in `$RUSTFLAGS` when set when using `--merged` * refactor(sqlx-cli): `cargo clean -p` works by name so rip out pkgid code * chore(sqlx-cli): Remove unused imports
1 parent 7cdb68b commit 2c67e2a

File tree

6 files changed

+30854
-26
lines changed

6 files changed

+30854
-26
lines changed

Cargo.lock

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sqlx-cli/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ glob = "0.3.0"
4646
openssl = { version = "0.10.38", optional = true }
4747
# workaround for https://github.com/rust-lang/rust/issues/29497
4848
remove_dir_all = "0.7.0"
49+
cargo_metadata = "0.14"
50+
filetime = "0.2"
4951

5052
backoff = { version = "0.4.0", features = ["futures", "tokio"] }
5153

sqlx-cli/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::time::Duration;
77
use crate::opt::{Command, ConnectOpts, DatabaseCommand, MigrateCommand};
88

99
mod database;
10+
mod metadata;
1011
// mod migration;
1112
// mod migrator;
1213
mod migrate;

sqlx-cli/src/metadata.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use anyhow::{Context, Result};
2+
use cargo_metadata::{
3+
Metadata as CargoMetadata, Package as MetadataPackage, PackageId as MetadataId,
4+
};
5+
6+
use std::{
7+
collections::{btree_map, BTreeMap, BTreeSet},
8+
path::{Path, PathBuf},
9+
str::FromStr,
10+
};
11+
12+
/// The minimal amount of package information we care about
13+
///
14+
/// The package's `name` is used to `cargo clean -p` specific crates while the `src_paths` are
15+
/// are used to trigger recompiles of packages within the workspace
16+
#[derive(Debug)]
17+
pub struct Package {
18+
name: String,
19+
src_paths: Vec<PathBuf>,
20+
}
21+
22+
impl Package {
23+
pub fn name(&self) -> &str {
24+
&self.name
25+
}
26+
27+
pub fn src_paths(&self) -> &[PathBuf] {
28+
&self.src_paths
29+
}
30+
}
31+
32+
impl From<&MetadataPackage> for Package {
33+
fn from(package: &MetadataPackage) -> Self {
34+
let name = package.name.clone();
35+
let src_paths = package
36+
.targets
37+
.iter()
38+
.map(|target| target.src_path.clone().into_std_path_buf())
39+
.collect();
40+
41+
Self { name, src_paths }
42+
}
43+
}
44+
45+
/// Contains metadata for the current project
46+
pub struct Metadata {
47+
/// Maps packages metadata id to the package
48+
///
49+
/// Currently `MetadataId` is used over `PkgId` because pkgid is not a UUID
50+
packages: BTreeMap<MetadataId, Package>,
51+
/// All of the crates in the current workspace
52+
workspace_members: Vec<MetadataId>,
53+
/// Maps each dependency to its set of dependents
54+
reverse_deps: BTreeMap<MetadataId, BTreeSet<MetadataId>>,
55+
/// The target directory of the project
56+
///
57+
/// Typically `target` at the workspace root, but can be overridden
58+
target_directory: PathBuf,
59+
}
60+
61+
impl Metadata {
62+
pub fn package(&self, id: &MetadataId) -> Option<&Package> {
63+
self.packages.get(id)
64+
}
65+
66+
pub fn entries<'this>(&'this self) -> btree_map::Iter<'this, MetadataId, Package> {
67+
self.packages.iter()
68+
}
69+
70+
pub fn workspace_members(&self) -> &[MetadataId] {
71+
&self.workspace_members
72+
}
73+
74+
pub fn target_directory(&self) -> &Path {
75+
&self.target_directory
76+
}
77+
78+
/// Gets all dependents (direct and transitive) of `id`
79+
pub fn all_dependents_of(&self, id: &MetadataId) -> BTreeSet<&MetadataId> {
80+
let mut dependents = BTreeSet::new();
81+
self.all_dependents_of_helper(id, &mut dependents);
82+
dependents
83+
}
84+
85+
fn all_dependents_of_helper<'this>(
86+
&'this self,
87+
id: &MetadataId,
88+
dependents: &mut BTreeSet<&'this MetadataId>,
89+
) {
90+
if let Some(immediate_dependents) = self.reverse_deps.get(id) {
91+
for immediate_dependent in immediate_dependents {
92+
if dependents.insert(immediate_dependent) {
93+
self.all_dependents_of_helper(&immediate_dependent, dependents);
94+
}
95+
}
96+
}
97+
}
98+
}
99+
100+
impl FromStr for Metadata {
101+
type Err = anyhow::Error;
102+
103+
fn from_str(s: &str) -> Result<Self, Self::Err> {
104+
let CargoMetadata {
105+
packages: metadata_packages,
106+
workspace_members,
107+
resolve,
108+
target_directory,
109+
..
110+
} = serde_json::from_str(s)?;
111+
112+
let mut packages = BTreeMap::new();
113+
for metadata_package in metadata_packages {
114+
let package = Package::from(&metadata_package);
115+
packages.insert(metadata_package.id, package);
116+
}
117+
118+
let mut reverse_deps: BTreeMap<_, BTreeSet<_>> = BTreeMap::new();
119+
let resolve =
120+
resolve.context("Resolving the dependency graph failed (old version of cargo)")?;
121+
for node in resolve.nodes {
122+
for dep in node.deps {
123+
let dependent = node.id.clone();
124+
let dependency = dep.pkg;
125+
reverse_deps
126+
.entry(dependency)
127+
.or_default()
128+
.insert(dependent);
129+
}
130+
}
131+
132+
let target_directory = target_directory.into_std_path_buf();
133+
134+
Ok(Self {
135+
packages,
136+
workspace_members,
137+
reverse_deps,
138+
target_directory,
139+
})
140+
}
141+
}

0 commit comments

Comments
 (0)