1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Utilities for parsing and generating Cargo.toml and related manifest files.
use std::collections::BTreeMap;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
/// Set of dependencies for a particular usage: final artifacts, tests, or
/// build scripts.
pub type DependencySet = BTreeMap<String, Dependency>;
/// Set of patches to replace upstream dependencies with local crates. Maps
/// arbitrary patch names to `CargoPatch` which includes the actual package name
/// and the local path.
pub type CargoPatchSet = BTreeMap<String, CargoPatch>;
/// A specific crate version.
pub use semver::Version;
/// A version constraint in a dependency spec. We don't use `semver::VersionReq`
/// since we only pass it through opaquely from third_party.toml to Cargo.toml.
/// Parsing it is unnecessary.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
// From serde's perspective we serialize and deserialize this as a plain string.
#[serde(transparent)]
pub struct VersionConstraint(pub String);
/// Parsed third_party.toml. This is a limited variant of Cargo.toml.
#[derive(Clone, Debug, Deserialize)]
pub struct ThirdPartyManifest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub workspace: Option<WorkspaceSpec>,
#[serde(flatten)]
pub dependency_spec: DependencySpec,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct WorkspaceSpec {
pub members: Vec<String>,
}
/// The sets of all types of dependencies for a manifest: regular, build script,
/// and test. This should be included in other structs with `#[serde(flatten)]`
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct DependencySpec {
/// Regular dependencies built into production code.
#[serde(
default,
skip_serializing_if = "DependencySet::is_empty",
serialize_with = "toml::ser::tables_last"
)]
pub dependencies: DependencySet,
/// Test-only dependencies.
#[serde(
default,
skip_serializing_if = "DependencySet::is_empty",
serialize_with = "toml::ser::tables_last"
)]
pub dev_dependencies: DependencySet,
/// Build script dependencies.
#[serde(
default,
skip_serializing_if = "DependencySet::is_empty",
serialize_with = "toml::ser::tables_last"
)]
pub build_dependencies: DependencySet,
}
/// A single crate dependency.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum Dependency {
/// A dependency of the form `foo = "1.0.11"`: just the package name as key
/// and the version as value. The sole field is the crate version.
Short(VersionConstraint),
/// A dependency that specifies other fields in the form of `foo = { ... }`
/// or `[dependencies.foo] ... `.
Full(FullDependency),
}
/// A single crate dependency with some extra fields from third_party.toml.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct FullDependency {
/// Version constraint on dependency.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<VersionConstraint>,
/// Required features.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub features: Vec<String>,
/// Whether this can be used directly from Chromium code, or only from other
/// third-party crates.
#[serde(default = "get_true", skip_serializing_if = "is_true")]
pub allow_first_party_usage: bool,
/// List of files generated by build.rs script.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub build_script_outputs: Vec<String>,
}
/// Representation of a Cargo.toml file.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoManifest {
pub package: CargoPackage,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub workspace: Option<WorkspaceSpec>,
#[serde(flatten)]
pub dependency_spec: DependencySpec,
#[serde(default, rename = "patch")]
pub patches: BTreeMap<String, CargoPatchSet>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoPackage {
pub name: String,
pub version: Version,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub authors: Vec<String>,
#[serde(default)]
pub edition: Edition,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub license: String,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(transparent)]
pub struct Edition(pub String);
impl Default for Edition {
fn default() -> Self {
Edition("2015".to_string())
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct CargoPatch {
pub path: String,
pub package: String,
}
// Used to set the serde default of a field to true.
fn get_true() -> bool {
true
}
fn is_true(b: &bool) -> bool {
*b
}
#[derive(Debug)]
pub struct PatchSpecification {
pub package_name: String,
pub patch_name: String,
pub path: PathBuf,
}
pub fn generate_fake_cargo_toml<Iter: IntoIterator<Item = PatchSpecification>>(
third_party_manifest: ThirdPartyManifest,
patches: Iter,
) -> CargoManifest {
let ThirdPartyManifest { workspace, mut dependency_spec, .. } = third_party_manifest;
// Hack: set all `allow_first_party_usage` fields to true so they are
// suppressed in the Cargo.toml.
for dep in [
dependency_spec.dependencies.values_mut(),
dependency_spec.build_dependencies.values_mut(),
dependency_spec.dev_dependencies.values_mut(),
]
.into_iter()
.flatten()
{
if let Dependency::Full(ref mut dep) = dep {
dep.allow_first_party_usage = true;
}
}
let mut patch_sections = CargoPatchSet::new();
// Generate patch section.
for PatchSpecification { package_name, patch_name, path } in patches {
patch_sections.insert(
patch_name,
CargoPatch { path: path.to_str().unwrap().to_string(), package: package_name },
);
}
let package = CargoPackage {
name: "chromium".to_string(),
version: Version::new(0, 1, 0),
authors: Vec::new(),
edition: Edition("2021".to_string()),
description: None,
license: "".to_string(),
};
CargoManifest {
package,
workspace,
dependency_spec,
patches: std::iter::once(("crates-io".to_string(), patch_sections)).collect(),
}
}
|