mirror of
https://github.com/rust-lang/cargo.git
synced 2026-01-25 04:16:45 +00:00
Add -Z json-target-spec (#16557)
This adds the `-Z json-target-spec` CLI flag to assist with using custom `.json` target spec files. `rustc` recently switched so that it requires `-Z unstable-options` when using custom spec files (https://github.com/rust-lang/rust/pull/151534). This can make it rather awkward to use spec files with cargo because it then requires setting RUSTFLAGS and RUSTDOCFLAGS to pass `-Zunstable-options`. It also ends up causing some confusing error messages. See the individual commits for more details. This ended up being a larger diff than I wanted. I'm not 100% certain this is worth it, but I think it significantly improves the experience using `.json` files, so I I'm leaning towards it. ### How to test and review this PR? Testing can be done with `rustc` built from https://github.com/rust-lang/rust/pull/151534 to ensure that everything passes after that PR (including setting `CARGO_RUN_BUILD_STD_TESTS=1`).
This commit is contained in:
@@ -200,9 +200,7 @@ impl TargetInfo {
|
||||
process.inherit_jobserver(client);
|
||||
}
|
||||
|
||||
if let CompileKind::Target(target) = kind {
|
||||
process.arg("--target").arg(target.rustc_target());
|
||||
}
|
||||
kind.add_target_arg(&mut process);
|
||||
|
||||
let crate_type_process = process.clone();
|
||||
const KNOWN_CRATE_TYPES: &[CrateType] = &[
|
||||
@@ -331,11 +329,7 @@ impl TargetInfo {
|
||||
.args(&rustflags)
|
||||
.env_remove("RUSTC_LOG");
|
||||
|
||||
if let CompileKind::Target(target) = kind {
|
||||
target_spec_process
|
||||
.arg("--target")
|
||||
.arg(target.rustc_target());
|
||||
}
|
||||
kind.add_target_arg(&mut target_spec_process);
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Metadata {
|
||||
@@ -965,7 +959,7 @@ impl<'gctx> RustcTargetData<'gctx> {
|
||||
let mut target_config = HashMap::new();
|
||||
let mut target_info = HashMap::new();
|
||||
let target_applies_to_host = gctx.target_applies_to_host()?;
|
||||
let host_target = CompileTarget::new(&rustc.host)?;
|
||||
let host_target = CompileTarget::new(&rustc.host, gctx.cli_unstable().json_target_spec)?;
|
||||
let host_info = TargetInfo::new(gctx, requested_kinds, &rustc, CompileKind::Host)?;
|
||||
|
||||
// This config is used for link overrides and choosing a linker.
|
||||
|
||||
@@ -5,6 +5,8 @@ use crate::util::errors::CargoResult;
|
||||
use crate::util::interning::InternedString;
|
||||
use crate::util::{GlobalContext, StableHasher, try_canonicalize};
|
||||
use anyhow::Context as _;
|
||||
use anyhow::bail;
|
||||
use cargo_util::ProcessBuilder;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fs;
|
||||
@@ -92,9 +94,15 @@ impl CompileKind {
|
||||
|
||||
if value.as_str() == "host-tuple" {
|
||||
let host_triple = env!("RUST_HOST_TARGET");
|
||||
Ok(CompileKind::Target(CompileTarget::new(host_triple)?))
|
||||
Ok(CompileKind::Target(CompileTarget::new(
|
||||
host_triple,
|
||||
gctx.cli_unstable().json_target_spec,
|
||||
)?))
|
||||
} else {
|
||||
Ok(CompileKind::Target(CompileTarget::new(value.as_str())?))
|
||||
Ok(CompileKind::Target(CompileTarget::new(
|
||||
value.as_str(),
|
||||
gctx.cli_unstable().json_target_spec,
|
||||
)?))
|
||||
}
|
||||
})
|
||||
// First collect into a set to deduplicate any `--target` passed
|
||||
@@ -132,6 +140,17 @@ impl CompileKind {
|
||||
CompileKind::Target(target) => target.fingerprint_hash(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the `--target` flag to the given [`ProcessBuilder`] if this is a
|
||||
/// non-host build.
|
||||
pub fn add_target_arg(&self, builder: &mut ProcessBuilder) {
|
||||
if let CompileKind::Target(target) = self {
|
||||
builder.arg("--target").arg(target.rustc_target());
|
||||
if matches!(target, CompileTarget::Json { .. }) {
|
||||
builder.arg("-Zunstable-options");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::Serialize for CompileKind {
|
||||
@@ -141,7 +160,7 @@ impl serde::ser::Serialize for CompileKind {
|
||||
{
|
||||
match self {
|
||||
CompileKind::Host => None::<&str>.serialize(s),
|
||||
CompileKind::Target(t) => Some(t.name).serialize(s),
|
||||
CompileKind::Target(t) => Some(t.rustc_target()).serialize(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,31 +183,39 @@ impl serde::ser::Serialize for CompileKind {
|
||||
/// file stem of JSON target files. For built-in rustc targets this is just an
|
||||
/// uninterpreted string basically.
|
||||
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord, Serialize)]
|
||||
pub struct CompileTarget {
|
||||
name: InternedString,
|
||||
pub enum CompileTarget {
|
||||
Tuple(InternedString),
|
||||
Json {
|
||||
short: InternedString,
|
||||
path: InternedString,
|
||||
},
|
||||
}
|
||||
|
||||
impl CompileTarget {
|
||||
pub fn new(name: &str) -> CargoResult<CompileTarget> {
|
||||
pub fn new(name: &str, unstable_json: bool) -> CargoResult<CompileTarget> {
|
||||
let name = name.trim();
|
||||
if name.is_empty() {
|
||||
anyhow::bail!("target was empty");
|
||||
bail!("target was empty");
|
||||
}
|
||||
if !name.ends_with(".json") {
|
||||
return Ok(CompileTarget { name: name.into() });
|
||||
return Ok(CompileTarget::Tuple(name.into()));
|
||||
}
|
||||
|
||||
if !unstable_json {
|
||||
bail!("`.json` target specs require -Zjson-target-spec");
|
||||
}
|
||||
|
||||
// If `name` ends in `.json` then it's likely a custom target
|
||||
// specification. Canonicalize the path to ensure that different builds
|
||||
// with different paths always produce the same result.
|
||||
let path = try_canonicalize(Path::new(name))
|
||||
.with_context(|| format!("target path {:?} is not a valid file", name))?;
|
||||
|
||||
let name = path
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(|_| anyhow::format_err!("target path is not valid unicode"))?;
|
||||
Ok(CompileTarget { name: name.into() })
|
||||
let p = try_canonicalize(Path::new(name))
|
||||
.with_context(|| format!("target path `{name}` is not a valid file"))?;
|
||||
let path = p
|
||||
.to_str()
|
||||
.ok_or_else(|| anyhow::format_err!("target path `{name}` is not valid unicode"))?
|
||||
.into();
|
||||
let short = p.file_stem().unwrap().to_str().unwrap().into();
|
||||
Ok(CompileTarget::Json { short, path })
|
||||
}
|
||||
|
||||
/// Returns the full unqualified name of this target, suitable for passing
|
||||
@@ -198,7 +225,10 @@ impl CompileTarget {
|
||||
/// of JSON target files this will be a full canonicalized path name for the
|
||||
/// current filesystem.
|
||||
pub fn rustc_target(&self) -> InternedString {
|
||||
self.name
|
||||
match self {
|
||||
CompileTarget::Tuple(name) => *name,
|
||||
CompileTarget::Json { path, .. } => *path,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a "short" version of the target name suitable for usage within
|
||||
@@ -208,34 +238,25 @@ impl CompileTarget {
|
||||
/// JSON target files this returns just the file stem (e.g. `foo` out of
|
||||
/// `foo.json`) instead of the full path.
|
||||
pub fn short_name(&self) -> &str {
|
||||
// Flexible target specifications often point at json files, so if it
|
||||
// looks like we've got one of those just use the file stem (the file
|
||||
// name without ".json") as a short name for this target. Note that the
|
||||
// `unwrap()` here should never trigger since we have a nonempty name
|
||||
// and it starts as utf-8 so it's always utf-8
|
||||
if self.name.ends_with(".json") {
|
||||
Path::new(&self.name).file_stem().unwrap().to_str().unwrap()
|
||||
} else {
|
||||
&self.name
|
||||
match self {
|
||||
CompileTarget::Tuple(name) => name,
|
||||
CompileTarget::Json { short, .. } => short,
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`CompileKind::fingerprint_hash`].
|
||||
pub fn fingerprint_hash(&self) -> u64 {
|
||||
let mut hasher = StableHasher::new();
|
||||
match self
|
||||
.name
|
||||
.ends_with(".json")
|
||||
.then(|| fs::read_to_string(self.name))
|
||||
{
|
||||
Some(Ok(contents)) => {
|
||||
match self {
|
||||
CompileTarget::Tuple(name) => name.hash(&mut hasher),
|
||||
CompileTarget::Json { path, .. } => {
|
||||
// This may have some performance concerns, since it is called
|
||||
// fairly often. If that ever seems worth fixing, consider
|
||||
// embedding this in `CompileTarget`.
|
||||
contents.hash(&mut hasher);
|
||||
}
|
||||
_ => {
|
||||
self.name.hash(&mut hasher);
|
||||
match fs::read_to_string(path) {
|
||||
Ok(contents) => contents.hash(&mut hasher),
|
||||
Err(_) => path.hash(&mut hasher),
|
||||
}
|
||||
}
|
||||
}
|
||||
Hasher::finish(&hasher)
|
||||
|
||||
@@ -864,9 +864,7 @@ fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResu
|
||||
add_path_args(bcx.ws, unit, &mut rustdoc);
|
||||
add_cap_lints(bcx, unit, &mut rustdoc);
|
||||
|
||||
if let CompileKind::Target(target) = unit.kind {
|
||||
rustdoc.arg("--target").arg(target.rustc_target());
|
||||
}
|
||||
unit.kind.add_target_arg(&mut rustdoc);
|
||||
let doc_dir = build_runner.files().out_dir(unit);
|
||||
rustdoc.arg("-o").arg(&doc_dir);
|
||||
rustdoc.args(&features_args(unit));
|
||||
@@ -1414,9 +1412,7 @@ fn build_base_args(
|
||||
}
|
||||
}
|
||||
|
||||
if let CompileKind::Target(n) = unit.kind {
|
||||
cmd.arg("--target").arg(n.rustc_target());
|
||||
}
|
||||
unit.kind.add_target_arg(cmd);
|
||||
|
||||
opt(
|
||||
cmd,
|
||||
|
||||
@@ -496,6 +496,7 @@ impl Artifact {
|
||||
artifacts: &[impl AsRef<str>],
|
||||
is_lib: bool,
|
||||
target: Option<&str>,
|
||||
unstable_json: bool,
|
||||
) -> CargoResult<Self> {
|
||||
let kinds = ArtifactKind::validate(
|
||||
artifacts
|
||||
@@ -506,7 +507,9 @@ impl Artifact {
|
||||
Ok(Artifact {
|
||||
inner: Arc::new(kinds),
|
||||
is_lib,
|
||||
target: target.map(ArtifactTarget::parse).transpose()?,
|
||||
target: target
|
||||
.map(|name| ArtifactTarget::parse(name, unstable_json))
|
||||
.transpose()?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -536,10 +539,10 @@ pub enum ArtifactTarget {
|
||||
}
|
||||
|
||||
impl ArtifactTarget {
|
||||
pub fn parse(target: &str) -> CargoResult<ArtifactTarget> {
|
||||
pub fn parse(target: &str, unstable_json: bool) -> CargoResult<ArtifactTarget> {
|
||||
Ok(match target {
|
||||
"target" => ArtifactTarget::BuildDependencyAssumeTarget,
|
||||
name => ArtifactTarget::Force(CompileTarget::new(name)?),
|
||||
name => ArtifactTarget::Force(CompileTarget::new(name, unstable_json)?),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -870,6 +870,7 @@ unstable_cli_options!(
|
||||
#[serde(deserialize_with = "deserialize_gitoxide_features")]
|
||||
gitoxide: Option<GitoxideFeatures> = ("Use gitoxide for the given git interactions, or all of them if no argument is given"),
|
||||
host_config: bool = ("Enable the `[host]` section in the .cargo/config.toml file"),
|
||||
json_target_spec: bool = ("Enable `.json` target spec files"),
|
||||
lockfile_path: bool = ("Enable the `resolver.lockfile-path` config option"),
|
||||
minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"),
|
||||
msrv_policy: bool = ("Enable rust-version aware policy within cargo"),
|
||||
@@ -1409,6 +1410,7 @@ impl CliUnstable {
|
||||
)?
|
||||
}
|
||||
"host-config" => self.host_config = parse_empty(k, v)?,
|
||||
"json-target-spec" => self.json_target_spec = parse_empty(k, v)?,
|
||||
"lockfile-path" => self.lockfile_path = parse_empty(k, v)?,
|
||||
"next-lockfile-bump" => self.next_lockfile_bump = parse_empty(k, v)?,
|
||||
"minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
|
||||
|
||||
@@ -811,6 +811,7 @@ impl<'a, 'gctx> FeatureResolver<'a, 'gctx> {
|
||||
pkg_id: PackageId,
|
||||
dep: &Dependency,
|
||||
lib_fk: FeaturesFor,
|
||||
unstable_json_spec: bool,
|
||||
) -> CargoResult<Vec<FeaturesFor>> {
|
||||
let Some(artifact) = dep.artifact() else {
|
||||
return Ok(vec![lib_fk]);
|
||||
@@ -844,7 +845,9 @@ impl<'a, 'gctx> FeatureResolver<'a, 'gctx> {
|
||||
ArtifactTarget::BuildDependencyAssumeTarget => {
|
||||
for kind in this.requested_targets {
|
||||
let target = match kind {
|
||||
CompileKind::Host => CompileTarget::new(&host_triple).unwrap(),
|
||||
CompileKind::Host => {
|
||||
CompileTarget::new(&host_triple, unstable_json_spec).unwrap()
|
||||
}
|
||||
CompileKind::Target(target) => *target,
|
||||
};
|
||||
activate_target(target)?;
|
||||
@@ -859,6 +862,7 @@ impl<'a, 'gctx> FeatureResolver<'a, 'gctx> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
let unstable_json_spec = self.ws.gctx().cli_unstable().json_target_spec;
|
||||
self.resolve
|
||||
.deps(pkg_id)
|
||||
.map(|(dep_id, deps)| {
|
||||
@@ -915,7 +919,8 @@ impl<'a, 'gctx> FeatureResolver<'a, 'gctx> {
|
||||
fk
|
||||
};
|
||||
|
||||
let dep_fks = artifact_features_for(self, pkg_id, dep, lib_fk)?;
|
||||
let dep_fks =
|
||||
artifact_features_for(self, pkg_id, dep, lib_fk, unstable_json_spec)?;
|
||||
Ok(dep_fks.into_iter().map(move |dep_fk| (dep, dep_fk)))
|
||||
})
|
||||
.flatten_ok()
|
||||
|
||||
@@ -221,9 +221,7 @@ pub fn print<'a>(
|
||||
if let Some(args) = target_rustc_args {
|
||||
process.args(args);
|
||||
}
|
||||
if let CompileKind::Target(t) = kind {
|
||||
process.arg("--target").arg(t.rustc_target());
|
||||
}
|
||||
kind.add_target_arg(&mut process);
|
||||
process.arg("--print").arg(print_opt_value);
|
||||
process.exec()?;
|
||||
}
|
||||
@@ -400,7 +398,10 @@ pub fn create_bcx<'a, 'gctx>(
|
||||
// If `--target` has not been specified, then the unit graph is built
|
||||
// assuming `--target $HOST` was specified. See
|
||||
// `rebuild_unit_graph_shared` for more on why this is done.
|
||||
let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?);
|
||||
let explicit_host_kind = CompileKind::Target(CompileTarget::new(
|
||||
&target_data.rustc.host,
|
||||
gctx.cli_unstable().json_target_spec,
|
||||
)?);
|
||||
let explicit_host_kinds: Vec<_> = build_config
|
||||
.requested_kinds
|
||||
.iter()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::core::compiler::{Compilation, CompileKind, Doctest, Unit, UnitHash, UnitOutput};
|
||||
use crate::core::compiler::{Compilation, Doctest, Unit, UnitHash, UnitOutput};
|
||||
use crate::core::profiles::PanicStrategy;
|
||||
use crate::core::shell::ColorChoice;
|
||||
use crate::core::shell::Verbosity;
|
||||
@@ -211,10 +211,7 @@ fn run_doc_tests(
|
||||
add_path_args(ws, unit, &mut p);
|
||||
p.arg("--test-run-directory").arg(unit.pkg.root());
|
||||
|
||||
if let CompileKind::Target(target) = unit.kind {
|
||||
// use `rustc_target()` to properly handle JSON target paths
|
||||
p.arg("--target").arg(target.rustc_target());
|
||||
}
|
||||
unit.kind.add_target_arg(&mut p);
|
||||
|
||||
if let Some((runtool, runtool_args)) = compilation.target_runner(unit.kind) {
|
||||
p.arg("--test-runtool").arg(runtool);
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
//!
|
||||
//! To learn the rationale behind this multi-layer index metadata loading,
|
||||
//! see [the documentation of the on-disk index cache](cache).
|
||||
use crate::core::Dependency;
|
||||
use crate::core::dependency::{Artifact, DepKind};
|
||||
use crate::core::{CliUnstable, Dependency};
|
||||
use crate::core::{PackageId, SourceId, Summary};
|
||||
use crate::sources::registry::{LoadResponse, RegistryData};
|
||||
use crate::util::IntoUrl;
|
||||
@@ -195,14 +195,18 @@ impl IndexSummary {
|
||||
}
|
||||
}
|
||||
|
||||
fn index_package_to_summary(pkg: &IndexPackage<'_>, source_id: SourceId) -> CargoResult<Summary> {
|
||||
fn index_package_to_summary(
|
||||
pkg: &IndexPackage<'_>,
|
||||
source_id: SourceId,
|
||||
cli_unstable: &CliUnstable,
|
||||
) -> CargoResult<Summary> {
|
||||
// ****CAUTION**** Please be extremely careful with returning errors, see
|
||||
// `IndexSummary::parse` for details
|
||||
let pkgid = PackageId::new(pkg.name.as_ref().into(), pkg.vers.clone(), source_id);
|
||||
let deps = pkg
|
||||
.deps
|
||||
.iter()
|
||||
.map(|dep| registry_dependency_into_dep(dep.clone(), source_id))
|
||||
.map(|dep| registry_dependency_into_dep(dep.clone(), source_id, cli_unstable))
|
||||
.collect::<CargoResult<Vec<_>>>()?;
|
||||
let mut features = pkg.features.clone();
|
||||
if let Some(features2) = pkg.features2.clone() {
|
||||
@@ -289,7 +293,7 @@ impl<'gctx> RegistryIndex<'gctx> {
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
let bindeps = self.gctx.cli_unstable().bindeps;
|
||||
let cli_unstable = self.gctx.cli_unstable();
|
||||
|
||||
let source_id = self.source_id;
|
||||
|
||||
@@ -308,7 +312,7 @@ impl<'gctx> RegistryIndex<'gctx> {
|
||||
.iter_mut()
|
||||
.filter_map(move |(k, v)| if req.matches(k) { Some(v) } else { None })
|
||||
.filter_map(move |maybe| {
|
||||
match maybe.parse(raw_data, source_id, bindeps) {
|
||||
match maybe.parse(raw_data, source_id, cli_unstable) {
|
||||
Ok(sum) => Some(sum),
|
||||
Err(e) => {
|
||||
info!("failed to parse `{}` registry package: {}", name, e);
|
||||
@@ -354,7 +358,7 @@ impl<'gctx> RegistryIndex<'gctx> {
|
||||
&name,
|
||||
self.source_id,
|
||||
load,
|
||||
self.gctx.cli_unstable().bindeps,
|
||||
self.gctx.cli_unstable(),
|
||||
&self.cache_manager,
|
||||
))?
|
||||
.unwrap_or_default();
|
||||
@@ -474,7 +478,7 @@ impl Summaries {
|
||||
name: &str,
|
||||
source_id: SourceId,
|
||||
load: &mut dyn RegistryData,
|
||||
bindeps: bool,
|
||||
cli_unstable: &CliUnstable,
|
||||
cache_manager: &CacheManager<'_>,
|
||||
) -> Poll<CargoResult<Option<Summaries>>> {
|
||||
// This is the file we're loading from cache or the index data.
|
||||
@@ -524,7 +528,7 @@ impl Summaries {
|
||||
// allow future cargo implementations to break the
|
||||
// interpretation of each line here and older cargo will simply
|
||||
// ignore the new lines.
|
||||
let summary = match IndexSummary::parse(line, source_id, bindeps) {
|
||||
let summary = match IndexSummary::parse(line, source_id, cli_unstable) {
|
||||
Ok(summary) => summary,
|
||||
Err(e) => {
|
||||
// This should only happen when there is an index
|
||||
@@ -611,13 +615,13 @@ impl MaybeIndexSummary {
|
||||
&mut self,
|
||||
raw_data: &[u8],
|
||||
source_id: SourceId,
|
||||
bindeps: bool,
|
||||
cli_unstable: &CliUnstable,
|
||||
) -> CargoResult<&IndexSummary> {
|
||||
let (start, end) = match self {
|
||||
MaybeIndexSummary::Unparsed { start, end } => (*start, *end),
|
||||
MaybeIndexSummary::Parsed(summary) => return Ok(summary),
|
||||
};
|
||||
let summary = IndexSummary::parse(&raw_data[start..end], source_id, bindeps)?;
|
||||
let summary = IndexSummary::parse(&raw_data[start..end], source_id, cli_unstable)?;
|
||||
*self = MaybeIndexSummary::Parsed(summary);
|
||||
match self {
|
||||
MaybeIndexSummary::Unparsed { .. } => unreachable!(),
|
||||
@@ -638,7 +642,11 @@ impl IndexSummary {
|
||||
///
|
||||
/// The `line` provided is expected to be valid JSON. It is supposed to be
|
||||
/// a [`IndexPackage`].
|
||||
fn parse(line: &[u8], source_id: SourceId, bindeps: bool) -> CargoResult<IndexSummary> {
|
||||
fn parse(
|
||||
line: &[u8],
|
||||
source_id: SourceId,
|
||||
cli_unstable: &CliUnstable,
|
||||
) -> CargoResult<IndexSummary> {
|
||||
// ****CAUTION**** Please be extremely careful with returning errors
|
||||
// from this function. Entries that error are not included in the
|
||||
// index cache, and can cause cargo to get confused when switching
|
||||
@@ -647,7 +655,7 @@ impl IndexSummary {
|
||||
// values carefully when making changes here.
|
||||
let index_summary = (|| {
|
||||
let index = serde_json::from_slice::<IndexPackage<'_>>(line)?;
|
||||
let summary = index_package_to_summary(&index, source_id)?;
|
||||
let summary = index_package_to_summary(&index, source_id, cli_unstable)?;
|
||||
Ok((index, summary))
|
||||
})();
|
||||
let (index, summary, valid) = match index_summary {
|
||||
@@ -679,14 +687,14 @@ impl IndexSummary {
|
||||
links: Default::default(),
|
||||
pubtime: Default::default(),
|
||||
};
|
||||
let summary = index_package_to_summary(&index, source_id)?;
|
||||
let summary = index_package_to_summary(&index, source_id, cli_unstable)?;
|
||||
(index, summary, false)
|
||||
}
|
||||
};
|
||||
let v = index.v.unwrap_or(1);
|
||||
tracing::trace!("json parsed registry {}/{}", index.name, index.vers);
|
||||
|
||||
let v_max = if bindeps {
|
||||
let v_max = if cli_unstable.bindeps {
|
||||
INDEX_V_MAX + 1
|
||||
} else {
|
||||
INDEX_V_MAX
|
||||
@@ -708,6 +716,7 @@ impl IndexSummary {
|
||||
fn registry_dependency_into_dep(
|
||||
dep: RegistryDependency<'_>,
|
||||
default: SourceId,
|
||||
cli_unstable: &CliUnstable,
|
||||
) -> CargoResult<Dependency> {
|
||||
let RegistryDependency {
|
||||
name,
|
||||
@@ -764,7 +773,12 @@ fn registry_dependency_into_dep(
|
||||
}
|
||||
|
||||
if let Some(artifacts) = artifact {
|
||||
let artifact = Artifact::parse(&artifacts, lib, bindep_target.as_deref())?;
|
||||
let artifact = Artifact::parse(
|
||||
&artifacts,
|
||||
lib,
|
||||
bindep_target.as_deref(),
|
||||
cli_unstable.json_target_spec,
|
||||
)?;
|
||||
dep.set_artifact(artifact);
|
||||
}
|
||||
|
||||
|
||||
@@ -1792,13 +1792,13 @@ note: only a feature named `default` will be enabled by default"
|
||||
let default_kind = normalized_package
|
||||
.default_target
|
||||
.as_ref()
|
||||
.map(|t| CompileTarget::new(&*t))
|
||||
.map(|t| CompileTarget::new(&*t, gctx.cli_unstable().json_target_spec))
|
||||
.transpose()?
|
||||
.map(CompileKind::Target);
|
||||
let forced_kind = normalized_package
|
||||
.forced_target
|
||||
.as_ref()
|
||||
.map(|t| CompileTarget::new(&*t))
|
||||
.map(|t| CompileTarget::new(&*t, gctx.cli_unstable().json_target_spec))
|
||||
.transpose()?
|
||||
.map(CompileKind::Target);
|
||||
let include = normalized_package
|
||||
@@ -2311,7 +2311,12 @@ fn dep_to_dependency<P: ResolveToPath + Clone>(
|
||||
orig.target.as_deref(),
|
||||
) {
|
||||
if manifest_ctx.gctx.cli_unstable().bindeps {
|
||||
let artifact = Artifact::parse(&artifact.0, is_lib, target)?;
|
||||
let artifact = Artifact::parse(
|
||||
&artifact.0,
|
||||
is_lib,
|
||||
target,
|
||||
manifest_ctx.gctx.cli_unstable().json_target_spec,
|
||||
)?;
|
||||
if dep.kind() != DepKind::Build
|
||||
&& artifact.target() == Some(ArtifactTarget::BuildDependencyAssumeTarget)
|
||||
{
|
||||
|
||||
@@ -100,6 +100,7 @@ Each new feature described below should explain how to use it.
|
||||
* [panic-immediate-abort](#panic-immediate-abort) --- Passes `-Cpanic=immediate-abort` to the compiler.
|
||||
* [compile-time-deps](#compile-time-deps) --- Perma-unstable feature for rust-analyzer
|
||||
* [fine-grain-locking](#fine-grain-locking) --- Use fine grain locking instead of locking the entire build cache
|
||||
* [target-spec-json](#target-spec-json) --- Allows the use of `.json` custom target specs.
|
||||
* rustdoc
|
||||
* [rustdoc-map](#rustdoc-map) --- Provides mappings for documentation to link to external sites like [docs.rs](https://docs.rs/).
|
||||
* [scrape-examples](#scrape-examples) --- Shows examples within documentation.
|
||||
@@ -2028,7 +2029,7 @@ cargo +nightly build --compile-time-deps -Z unstable-options
|
||||
cargo +nightly check --compile-time-deps --all-targets -Z unstable-options
|
||||
```
|
||||
|
||||
# `rustc-unicode`
|
||||
## `rustc-unicode`
|
||||
* Tracking Issue: [rust#148607](https://github.com/rust-lang/rust/issues/148607)
|
||||
|
||||
Enable `rustc`'s unicode error format in Cargo's error messages
|
||||
@@ -2045,6 +2046,17 @@ so that `cargo doc` can merge cross-crate information
|
||||
from separate output directories,
|
||||
and run `rustdoc` in parallel.
|
||||
|
||||
## target-spec-json
|
||||
* Tracking Issue: [rust-lang/rust#151528](https://github.com/rust-lang/rust/issues/151528)
|
||||
|
||||
The `-Z target-spec-json` CLI flag enables the ability to use [custom target spec JSON files](https://doc.rust-lang.org/nightly/rustc/targets/custom.html) as a target.
|
||||
|
||||
```console
|
||||
cargo +nightly build --target my-target.json -Z target-spec-json
|
||||
```
|
||||
|
||||
This usually must be combined with [build-std](#build-std).
|
||||
|
||||
# Stabilized and removed features
|
||||
|
||||
## Compile progress
|
||||
|
||||
@@ -312,7 +312,8 @@ fn cross_custom() {
|
||||
.file("custom-target.json", target_spec_json())
|
||||
.build();
|
||||
|
||||
p.cargo("build --target custom-target.json -v")
|
||||
p.cargo("build --target custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.build_std_arg("core")
|
||||
.run();
|
||||
}
|
||||
@@ -352,7 +353,8 @@ fn custom_test_framework() {
|
||||
paths.insert(0, sysroot_bin);
|
||||
let new_path = env::join_paths(paths).unwrap();
|
||||
|
||||
p.cargo("test --target target.json --no-run -v")
|
||||
p.cargo("test --target target.json --no-run -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.env("PATH", new_path)
|
||||
.build_std_arg("core")
|
||||
.run();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg width="1255px" height="992px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="1255px" height="1010px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -64,65 +64,67 @@
|
||||
</tspan>
|
||||
<tspan x="10px" y="442px"><tspan> -Z host-config Enable the `[host]` section in the .cargo/config.toml file</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="460px"><tspan> -Z lockfile-path Enable the `resolver.lockfile-path` config option</tspan>
|
||||
<tspan x="10px" y="460px"><tspan> -Z json-target-spec Enable `.json` target spec files</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="478px"><tspan> -Z minimal-versions Resolve minimal dependency versions instead of maximum</tspan>
|
||||
<tspan x="10px" y="478px"><tspan> -Z lockfile-path Enable the `resolver.lockfile-path` config option</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="496px"><tspan> -Z msrv-policy Enable rust-version aware policy within cargo</tspan>
|
||||
<tspan x="10px" y="496px"><tspan> -Z minimal-versions Resolve minimal dependency versions instead of maximum</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="514px"><tspan> -Z mtime-on-use Configure Cargo to update the mtime of used files</tspan>
|
||||
<tspan x="10px" y="514px"><tspan> -Z msrv-policy Enable rust-version aware policy within cargo</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="532px"><tspan> -Z no-embed-metadata Avoid embedding metadata in library artifacts</tspan>
|
||||
<tspan x="10px" y="532px"><tspan> -Z mtime-on-use Configure Cargo to update the mtime of used files</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="550px"><tspan> -Z no-index-update Do not update the registry index even if the cache is outdated</tspan>
|
||||
<tspan x="10px" y="550px"><tspan> -Z no-embed-metadata Avoid embedding metadata in library artifacts</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="568px"><tspan> -Z panic-abort-tests Enable support to run tests with -Cpanic=abort</tspan>
|
||||
<tspan x="10px" y="568px"><tspan> -Z no-index-update Do not update the registry index even if the cache is outdated</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="586px"><tspan> -Z panic-immediate-abort Enable setting `panic = "immediate-abort"` in profiles</tspan>
|
||||
<tspan x="10px" y="586px"><tspan> -Z panic-abort-tests Enable support to run tests with -Cpanic=abort</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="604px"><tspan> -Z profile-hint-mostly-unused Enable the `hint-mostly-unused` setting in profiles to mark a crate as mostly unused.</tspan>
|
||||
<tspan x="10px" y="604px"><tspan> -Z panic-immediate-abort Enable setting `panic = "immediate-abort"` in profiles</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="622px"><tspan> -Z profile-rustflags Enable the `rustflags` option in profiles in .cargo/config.toml file</tspan>
|
||||
<tspan x="10px" y="622px"><tspan> -Z profile-hint-mostly-unused Enable the `hint-mostly-unused` setting in profiles to mark a crate as mostly unused.</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="640px"><tspan> -Z public-dependency Respect a dependency's `public` field in Cargo.toml to control public/private dependencies</tspan>
|
||||
<tspan x="10px" y="640px"><tspan> -Z profile-rustflags Enable the `rustflags` option in profiles in .cargo/config.toml file</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="658px"><tspan> -Z publish-timeout Enable the `publish.timeout` key in .cargo/config.toml file</tspan>
|
||||
<tspan x="10px" y="658px"><tspan> -Z public-dependency Respect a dependency's `public` field in Cargo.toml to control public/private dependencies</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="676px"><tspan> -Z root-dir Set the root directory relative to which paths are printed (defaults to workspace root)</tspan>
|
||||
<tspan x="10px" y="676px"><tspan> -Z publish-timeout Enable the `publish.timeout` key in .cargo/config.toml file</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="694px"><tspan> -Z rustc-unicode Enable `rustc`'s unicode error format in Cargo's error messages</tspan>
|
||||
<tspan x="10px" y="694px"><tspan> -Z root-dir Set the root directory relative to which paths are printed (defaults to workspace root)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="712px"><tspan> -Z rustdoc-depinfo Use dep-info files in rustdoc rebuild detection</tspan>
|
||||
<tspan x="10px" y="712px"><tspan> -Z rustc-unicode Enable `rustc`'s unicode error format in Cargo's error messages</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="730px"><tspan> -Z rustdoc-map Allow passing external documentation mappings to rustdoc</tspan>
|
||||
<tspan x="10px" y="730px"><tspan> -Z rustdoc-depinfo Use dep-info files in rustdoc rebuild detection</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="748px"><tspan> -Z rustdoc-mergeable-info Use rustdoc mergeable cross-crate-info files</tspan>
|
||||
<tspan x="10px" y="748px"><tspan> -Z rustdoc-map Allow passing external documentation mappings to rustdoc</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="766px"><tspan> -Z rustdoc-scrape-examples Allows Rustdoc to scrape code examples from reverse-dependencies</tspan>
|
||||
<tspan x="10px" y="766px"><tspan> -Z rustdoc-mergeable-info Use rustdoc mergeable cross-crate-info files</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="784px"><tspan> -Z sbom Enable the `sbom` option in build config in .cargo/config.toml file</tspan>
|
||||
<tspan x="10px" y="784px"><tspan> -Z rustdoc-scrape-examples Allows Rustdoc to scrape code examples from reverse-dependencies</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="802px"><tspan> -Z script Enable support for single-file, `.rs` packages</tspan>
|
||||
<tspan x="10px" y="802px"><tspan> -Z sbom Enable the `sbom` option in build config in .cargo/config.toml file</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="820px"><tspan> -Z section-timings Enable support for extended compilation sections in --timings output</tspan>
|
||||
<tspan x="10px" y="820px"><tspan> -Z script Enable support for single-file, `.rs` packages</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="838px"><tspan> -Z target-applies-to-host Enable the `target-applies-to-host` key in the .cargo/config.toml file</tspan>
|
||||
<tspan x="10px" y="838px"><tspan> -Z section-timings Enable support for extended compilation sections in --timings output</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="856px"><tspan> -Z trim-paths Enable the `trim-paths` option in profiles</tspan>
|
||||
<tspan x="10px" y="856px"><tspan> -Z target-applies-to-host Enable the `target-applies-to-host` key in the .cargo/config.toml file</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="874px"><tspan> -Z unstable-options Allow the usage of unstable options</tspan>
|
||||
<tspan x="10px" y="874px"><tspan> -Z trim-paths Enable the `trim-paths` option in profiles</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="892px"><tspan> -Z warnings Allow use of the build.warnings config key</tspan>
|
||||
<tspan x="10px" y="892px"><tspan> -Z unstable-options Allow the usage of unstable options</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="910px">
|
||||
<tspan x="10px" y="910px"><tspan> -Z warnings Allow use of the build.warnings config key</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="928px"><tspan>Run with `cargo -Z [FLAG] [COMMAND]`</tspan>
|
||||
<tspan x="10px" y="928px">
|
||||
</tspan>
|
||||
<tspan x="10px" y="946px">
|
||||
<tspan x="10px" y="946px"><tspan>Run with `cargo -Z [FLAG] [COMMAND]`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="964px"><tspan>See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about these flags.</tspan>
|
||||
<tspan x="10px" y="964px">
|
||||
</tspan>
|
||||
<tspan x="10px" y="982px">
|
||||
<tspan x="10px" y="982px"><tspan>See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html for more information about these flags.</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="1000px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 8.0 KiB |
@@ -34,6 +34,57 @@ pub trait Copy {
|
||||
}
|
||||
"#;
|
||||
|
||||
#[cargo_test]
|
||||
fn custom_target_gated() {
|
||||
// Checks that .json targets require the -Z option.
|
||||
let p = project()
|
||||
.file("src/lib.rs", "")
|
||||
.file("bar/Cargo.toml", &basic_manifest("bar", "0.0.0"))
|
||||
.file("bar/src/main.rs", "fn main() {}")
|
||||
.build();
|
||||
p.cargo("build --target custom-target.json")
|
||||
.with_status(101)
|
||||
.with_stderr_data(str![[r#"
|
||||
[ERROR] `.json` target specs require -Zjson-target-spec
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
// Same with config settings.
|
||||
p.cargo("build")
|
||||
.env("CARGO_BUILD_TARGET", "custom-target.json")
|
||||
.with_status(101)
|
||||
.with_stderr_data(str![[r#"
|
||||
[ERROR] `.json` target specs require -Zjson-target-spec
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
|
||||
// And artifact dependencies.
|
||||
p.change_file(
|
||||
"Cargo.toml",
|
||||
r#"
|
||||
[package]
|
||||
name = "foo"
|
||||
edition = "2015"
|
||||
|
||||
[dependencies]
|
||||
bar = { path = "bar/", artifact = "bin", target = "custom-target.json" }
|
||||
"#,
|
||||
);
|
||||
p.cargo("build -Z bindeps")
|
||||
.masquerade_as_nightly_cargo(&["bindeps"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(str![[r#"
|
||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
||||
|
||||
Caused by:
|
||||
`.json` target specs require -Zjson-target-spec
|
||||
|
||||
"#]])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test(nightly, reason = "requires features no_core, lang_items")]
|
||||
fn custom_target_minimal() {
|
||||
let p = project()
|
||||
@@ -51,13 +102,16 @@ fn custom_target_minimal() {
|
||||
.file("custom-target.json", target_spec_json())
|
||||
.build();
|
||||
|
||||
p.cargo("build --lib --target custom-target.json -v").run();
|
||||
p.cargo("build --lib --target src/../custom-target.json -v")
|
||||
p.cargo("build --lib --target custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.run();
|
||||
p.cargo("build --lib --target src/../custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.run();
|
||||
|
||||
// Ensure that the correct style of flag is passed to --target with doc tests.
|
||||
p.cargo("test --doc --target src/../custom-target.json -v")
|
||||
.masquerade_as_nightly_cargo(&["no_core", "lang_items"])
|
||||
p.cargo("test --doc --target src/../custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["no_core", "lang_items", "json-target-spec"])
|
||||
.with_stderr_data(str![[r#"
|
||||
[FRESH] foo v0.0.1 ([ROOT]/foo)
|
||||
[FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||
@@ -119,7 +173,9 @@ fn custom_target_dependency() {
|
||||
.file("custom-target.json", target_spec_json())
|
||||
.build();
|
||||
|
||||
p.cargo("build --lib --target custom-target.json -v").run();
|
||||
p.cargo("build --lib --target custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test(nightly, reason = "requires features no_core, lang_items")]
|
||||
@@ -138,7 +194,9 @@ fn custom_bin_target() {
|
||||
.file("custom-bin-target.json", target_spec_json())
|
||||
.build();
|
||||
|
||||
p.cargo("build --target custom-bin-target.json -v").run();
|
||||
p.cargo("build --target custom-bin-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.run();
|
||||
}
|
||||
|
||||
#[cargo_test(nightly, reason = "requires features no_core, lang_items")]
|
||||
@@ -159,8 +217,11 @@ fn changing_spec_rebuilds() {
|
||||
.file("custom-target.json", target_spec_json())
|
||||
.build();
|
||||
|
||||
p.cargo("build --lib --target custom-target.json -v").run();
|
||||
p.cargo("build --lib --target custom-target.json -v")
|
||||
p.cargo("build --lib --target custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.run();
|
||||
p.cargo("build --lib --target custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.with_stderr_data(str![[r#"
|
||||
[FRESH] foo v0.0.1 ([ROOT]/foo)
|
||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||
@@ -172,7 +233,8 @@ fn changing_spec_rebuilds() {
|
||||
// Some arbitrary change that I hope is safe.
|
||||
let spec = spec.replace('{', "{\n\"vendor\": \"unknown\",\n");
|
||||
fs::write(&spec_path, spec).unwrap();
|
||||
p.cargo("build --lib --target custom-target.json -v")
|
||||
p.cargo("build --lib --target custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.with_stderr_data(str![[r#"
|
||||
[COMPILING] foo v0.0.1 ([ROOT]/foo)
|
||||
[RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]
|
||||
@@ -204,7 +266,8 @@ fn changing_spec_relearns_crate_types() {
|
||||
.file("custom-target.json", target_spec_json())
|
||||
.build();
|
||||
|
||||
p.cargo("build --lib --target custom-target.json -v")
|
||||
p.cargo("build --lib --target custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.with_status(101)
|
||||
.with_stderr_data(str![[r#"
|
||||
[ERROR] cannot produce cdylib for `foo v0.1.0 ([ROOT]/foo)` [..]
|
||||
@@ -218,7 +281,8 @@ fn changing_spec_relearns_crate_types() {
|
||||
let spec = spec.replace('{', "{\n\"dynamic-linking\": true,\n");
|
||||
fs::write(&spec_path, spec).unwrap();
|
||||
|
||||
p.cargo("build --lib --target custom-target.json -v")
|
||||
p.cargo("build --lib --target custom-target.json -v -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.with_stderr_data(str![[r#"
|
||||
[COMPILING] foo v0.1.0 ([ROOT]/foo)
|
||||
[RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]
|
||||
@@ -248,7 +312,8 @@ fn custom_target_ignores_filepath() {
|
||||
.build();
|
||||
|
||||
// Should build the library the first time.
|
||||
p.cargo("build --lib --target a/custom-target.json")
|
||||
p.cargo("build --lib --target a/custom-target.json -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.with_stderr_data(str![[r#"
|
||||
[COMPILING] foo v0.0.1 ([ROOT]/foo)
|
||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||
@@ -257,7 +322,8 @@ fn custom_target_ignores_filepath() {
|
||||
.run();
|
||||
|
||||
// But not the second time, even though the path to the custom target is different.
|
||||
p.cargo("build --lib --target b/custom-target.json")
|
||||
p.cargo("build --lib --target b/custom-target.json -Zjson-target-spec")
|
||||
.masquerade_as_nightly_cargo(&["json_target_spec"])
|
||||
.with_stderr_data(str![[r#"
|
||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
||||
|
||||
|
||||
Reference in New Issue
Block a user