diff --git a/crates/header-translator/src/availability.rs b/crates/header-translator/src/availability.rs
index 264909101..ddef2a4b9 100644
--- a/crates/header-translator/src/availability.rs
+++ b/crates/header-translator/src/availability.rs
@@ -6,14 +6,90 @@ use clang::{Entity, PlatformAvailability, Version};
use crate::context::Context;
#[derive(Debug, Clone, PartialEq, Default)]
-struct Unavailable {
- ios: bool,
- ios_app_extension: bool,
- macos: bool,
- macos_app_extension: bool,
- maccatalyst: bool,
- watchos: bool,
- tvos: bool,
+pub struct Unavailable {
+ pub(crate) ios: bool,
+ pub(crate) ios_app_extension: bool,
+ pub(crate) macos: bool,
+ pub(crate) macos_app_extension: bool,
+ pub(crate) maccatalyst: bool,
+ pub(crate) watchos: bool,
+ pub(crate) tvos: bool,
+ pub(crate) library_unavailablility: Option>,
+}
+
+impl Unavailable {
+ fn list_unavailable_oses(&self) -> Vec<&str> {
+ let mut unavailable_oses = Vec::new();
+ if self.ios
+ && !self
+ .library_unavailablility
+ .as_ref()
+ .map(|u| u.ios)
+ .unwrap_or_else(|| false)
+ {
+ unavailable_oses.push("ios");
+ }
+ if self.macos
+ && !self
+ .library_unavailablility
+ .as_ref()
+ .map(|u| u.macos)
+ .unwrap_or_else(|| false)
+ {
+ unavailable_oses.push("macos");
+ }
+ if self.tvos
+ && !self
+ .library_unavailablility
+ .as_ref()
+ .map(|u| u.tvos)
+ .unwrap_or_else(|| false)
+ {
+ unavailable_oses.push("tvos");
+ }
+ if self.watchos
+ && !self
+ .library_unavailablility
+ .as_ref()
+ .map(|u| u.watchos)
+ .unwrap_or_else(|| false)
+ {
+ unavailable_oses.push("watchos");
+ }
+ unavailable_oses
+ }
+
+ /// In some cases of enums, we need to know the availability of the parent enum and the enum
+ /// variant.
+ pub fn merge(&self, other: &Self) -> Self {
+ Self {
+ ios: self.ios || other.ios,
+ ios_app_extension: self.ios_app_extension || other.ios_app_extension,
+ macos: self.macos || other.macos,
+ macos_app_extension: self.macos_app_extension || other.macos_app_extension,
+ tvos: self.tvos || other.tvos,
+ watchos: self.watchos || other.watchos,
+ maccatalyst: self.maccatalyst || other.maccatalyst,
+ library_unavailablility: self.library_unavailablility.clone(),
+ }
+ }
+}
+impl fmt::Display for Unavailable {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let unavailable_oses = self.list_unavailable_oses();
+ let unavailable_oses = unavailable_oses
+ .iter()
+ .map(|os| format!("target_os = \"{os}\""))
+ .collect::>()
+ .join(",");
+ if unavailable_oses.len() > 1 {
+ write!(f, "#[cfg(not(any({unavailable_oses})))]")?;
+ }
+ if unavailable_oses.len() == 1 {
+ write!(f, "#[cfg(not({unavailable_oses}))]")?;
+ }
+ Ok(())
+ }
}
#[derive(Debug, Clone, PartialEq, Default)]
@@ -29,7 +105,7 @@ struct Versions {
#[derive(Debug, Clone, PartialEq)]
pub struct Availability {
- unavailable: Unavailable,
+ pub(crate) unavailable: Unavailable,
introduced: Versions,
deprecated: Versions,
message: Option,
@@ -37,12 +113,18 @@ pub struct Availability {
}
impl Availability {
- pub fn parse(entity: &Entity<'_>, _context: &Context<'_>) -> Self {
+ pub fn parse(entity: &Entity<'_>, context: &Context<'_>) -> Self {
let availabilities = entity
.get_platform_availability()
.expect("platform availability");
- let mut unavailable = Unavailable::default();
+ let mut unavailable = Unavailable {
+ library_unavailablility: context
+ .library_unavailability
+ .as_ref()
+ .map(|l| Box::new(l.clone())),
+ ..Default::default()
+ };
let mut introduced = Versions::default();
let mut deprecated = Versions::default();
let mut message = None;
@@ -157,7 +239,8 @@ impl fmt::Display for Availability {
}
}
}
- // TODO: Emit `cfg` attributes based on `self.unavailable`
+ write!(f, "{}", self.unavailable)?;
+
// TODO: Emit availability checks based on `self.introduced`
Ok(())
}
diff --git a/crates/header-translator/src/config.rs b/crates/header-translator/src/config.rs
index 8c43db029..1387a2916 100644
--- a/crates/header-translator/src/config.rs
+++ b/crates/header-translator/src/config.rs
@@ -5,6 +5,7 @@ use std::path::Path;
use serde::Deserialize;
+use crate::availability::Unavailable;
use crate::data;
use crate::stmt::{Derives, Mutability};
@@ -80,6 +81,18 @@ pub struct LibraryData {
#[serde(default)]
pub watchos: Option,
}
+impl LibraryData {
+ pub(crate) fn unavailability(&self) -> Unavailable {
+ Unavailable {
+ ios: self.ios.is_none(),
+ macos: self.macos.is_none(),
+ tvos: self.tvos.is_none(),
+ watchos: self.watchos.is_none(),
+ maccatalyst: self.maccatalyst.is_none(),
+ ..Default::default()
+ }
+ }
+}
#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
diff --git a/crates/header-translator/src/context.rs b/crates/header-translator/src/context.rs
index bbdb06009..a882c3739 100644
--- a/crates/header-translator/src/context.rs
+++ b/crates/header-translator/src/context.rs
@@ -6,6 +6,7 @@ use apple_sdk::SdkPath;
use clang::source::Location;
use clang::Entity;
+use crate::availability::Unavailable;
use crate::config::Config;
pub struct Context<'a> {
@@ -14,6 +15,7 @@ pub struct Context<'a> {
framework_dir: PathBuf,
include_dir: PathBuf,
system_headers: HashSet<&'static Path>,
+ pub library_unavailability: Option,
}
impl<'a> Context<'a> {
@@ -29,6 +31,7 @@ impl<'a> Context<'a> {
Path::new("objc/NSObject.h"),
Path::new("objc/NSObjCRuntime.h"),
]),
+ library_unavailability: None,
}
}
diff --git a/crates/header-translator/src/library.rs b/crates/header-translator/src/library.rs
index c1051d189..4c22900f4 100644
--- a/crates/header-translator/src/library.rs
+++ b/crates/header-translator/src/library.rs
@@ -4,17 +4,20 @@ use std::fs;
use std::io;
use std::path::Path;
+use crate::availability::Unavailable;
use crate::file::{File, FILE_PRELUDE};
#[derive(Debug, PartialEq, Default)]
pub struct Library {
pub files: BTreeMap,
+ pub unavailability: Unavailable,
}
impl Library {
- pub fn new() -> Self {
+ pub(crate) fn new(unavailability: Unavailable) -> Self {
Self {
files: BTreeMap::new(),
+ unavailability,
}
}
@@ -60,16 +63,16 @@ impl fmt::Display for Library {
// NOTE: some SDK files have '+' in the file name
let name = name.replace('+', "_");
for stmt in &file.stmts {
- let mut iter = stmt.declared_types();
- if let Some(item) = iter.next() {
- // Use a set to deduplicate features, and to have them in
- // a consistent order
- let mut features = BTreeSet::new();
- stmt.visit_required_types(|item| {
- if let Some(feature) = item.feature() {
- features.insert(format!("feature = \"{feature}\""));
- }
- });
+ // Use a set to deduplicate features, and to have them in
+ // a consistent order
+ let mut features = BTreeSet::new();
+ stmt.visit_required_types(|item| {
+ if let Some(feature) = item.feature() {
+ features.insert(format!("feature = \"{feature}\""));
+ }
+ });
+
+ for (item, unavailability) in stmt.declared_types() {
match features.len() {
0 => {}
1 => {
@@ -87,12 +90,8 @@ impl fmt::Display for Library {
)?;
}
}
-
- writeln!(f, "pub use self::__{name}::{{{item}")?;
- for item in iter {
- writeln!(f, ", {item}")?;
- }
- writeln!(f, "}};")?;
+ write!(f, "{unavailability}")?;
+ writeln!(f, "pub use self::__{name}::{{{item}}};")?;
}
}
}
diff --git a/crates/header-translator/src/main.rs b/crates/header-translator/src/main.rs
index 0abf71e23..4e7510ced 100644
--- a/crates/header-translator/src/main.rs
+++ b/crates/header-translator/src/main.rs
@@ -197,7 +197,7 @@ fn parse_sdk(index: &Index<'_>, sdk: &SdkPath, llvm_target: &str, config: &Confi
let tu = get_translation_unit(index, sdk, llvm_target);
let mut preprocessing = true;
- let mut result = Output::from_libraries(config.libraries.keys());
+ let mut result = Output::from_libraries(&config.libraries);
let mut library_span = None;
let mut library_span_name = String::new();
@@ -270,6 +270,7 @@ fn parse_sdk(index: &Index<'_>, sdk: &SdkPath, llvm_target: &str, config: &Confi
preprocessing = false;
// No more includes / macro expansions after this line
let file = library.files.get_mut(&file_name).expect("file");
+ context.library_unavailability = Some(library.unavailability.clone());
for stmt in Stmt::parse(&entity, &context) {
file.add_stmt(stmt);
}
diff --git a/crates/header-translator/src/output.rs b/crates/header-translator/src/output.rs
index f274a56fc..c783f7391 100644
--- a/crates/header-translator/src/output.rs
+++ b/crates/header-translator/src/output.rs
@@ -1,7 +1,8 @@
+use std::collections::HashMap;
use std::collections::{BTreeMap, BTreeSet};
use std::str::FromStr;
-use crate::config::Config;
+use crate::config::{Config, LibraryData};
use crate::library::Library;
use crate::stmt::Stmt;
@@ -11,10 +12,10 @@ pub struct Output {
}
impl Output {
- pub fn from_libraries(libraries: impl IntoIterator- >) -> Self {
+ pub fn from_libraries(libraries: &HashMap) -> Self {
let libraries = libraries
- .into_iter()
- .map(|name| (name.into(), Library::new()))
+ .iter()
+ .map(|(name, library_data)| (name.into(), Library::new(library_data.unavailability())))
.collect();
Self { libraries }
}
diff --git a/crates/header-translator/src/stmt.rs b/crates/header-translator/src/stmt.rs
index b98f1a962..ae39e0080 100644
--- a/crates/header-translator/src/stmt.rs
+++ b/crates/header-translator/src/stmt.rs
@@ -7,7 +7,7 @@ use std::mem;
use clang::{Entity, EntityKind, EntityVisitResult};
-use crate::availability::Availability;
+use crate::availability::{Availability, Unavailable};
use crate::config::{ClassData, MethodData};
use crate::context::Context;
use crate::expr::Expr;
@@ -579,7 +579,7 @@ impl Stmt {
cls: id.clone(),
generics: generics.clone(),
category: ItemIdentifier::with_name(None, entity, context),
- availability: Availability::parse(entity, context),
+ availability: availability.clone(),
superclasses: superclasses.clone(),
methods,
description: Some(format!(
@@ -1156,30 +1156,66 @@ impl Stmt {
}
}
- pub(crate) fn declared_types(&self) -> impl Iterator
- {
+ pub(crate) fn declared_types(&self) -> impl Iterator
- {
match self {
- Stmt::ClassDecl { id, skipped, .. } => {
+ Stmt::ClassDecl {
+ id,
+ skipped,
+ availability,
+ ..
+ } => {
if *skipped {
None
} else {
- Some(&*id.name)
+ Some((&*id.name, availability.unavailable.clone()))
}
}
Stmt::Methods { .. } => None,
- Stmt::ProtocolDecl { id, .. } => Some(&*id.name),
+ Stmt::ProtocolDecl {
+ id, availability, ..
+ } => Some((&*id.name, availability.unavailable.clone())),
Stmt::ProtocolImpl { .. } => None,
- Stmt::StructDecl { id, .. } => Some(&*id.name),
- Stmt::EnumDecl { id, .. } => id.name.as_deref(),
- Stmt::VarDecl { id, .. } => Some(&*id.name),
- Stmt::FnDecl { id, body, .. } if body.is_none() => Some(&*id.name),
+ Stmt::StructDecl {
+ id, availability, ..
+ } => Some((&*id.name, availability.unavailable.clone())),
+ Stmt::EnumDecl {
+ id, availability, ..
+ } => id
+ .name
+ .as_deref()
+ .map(|name| (name, availability.unavailable.clone())),
+ Stmt::VarDecl {
+ id, availability, ..
+ } => Some((&*id.name, availability.unavailable.clone())),
+ Stmt::FnDecl {
+ id,
+ body,
+ availability,
+ ..
+ } if body.is_none() => Some((&*id.name, availability.unavailable.clone())),
// TODO
Stmt::FnDecl { .. } => None,
- Stmt::AliasDecl { id, .. } => Some(&*id.name),
+ Stmt::AliasDecl {
+ id, availability, ..
+ } => Some((&*id.name, availability.unavailable.clone())),
}
.into_iter()
.chain({
- if let Stmt::EnumDecl { variants, .. } = self {
- variants.iter().map(|(name, _, _)| &**name).collect()
+ if let Stmt::EnumDecl {
+ variants,
+ availability,
+ ..
+ } = self
+ {
+ variants
+ .iter()
+ .map(|(name, variant_availability, _)| {
+ let unavailable = availability
+ .unavailable
+ .merge(&variant_availability.unavailable);
+ (&**name, unavailable)
+ })
+ .collect()
} else {
vec![]
}
@@ -1304,6 +1340,7 @@ impl fmt::Display for Stmt {
writeln!(f)?;
+ write!(f, "{availability}")?;
if let Some(feature) = &main_feature_gate {
writeln!(f, " #[cfg(feature = \"{feature}\")]")?;
}
@@ -1361,11 +1398,13 @@ impl fmt::Display for Stmt {
generics,
category,
// TODO: Output `#[deprecated]` only on categories
- availability: _,
+ availability,
superclasses,
methods,
description,
} => {
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "extern_methods!(")?;
if let Some(description) = description {
writeln!(f, " /// {description}")?;
@@ -1379,6 +1418,7 @@ impl fmt::Display for Stmt {
if let Some(feature) = cls.feature() {
writeln!(f, " #[cfg(feature = \"{feature}\")]")?;
}
+ write!(f, "{unavailable}")?;
writeln!(
f,
" unsafe impl{} {}{} {{",
@@ -1434,6 +1474,7 @@ impl fmt::Display for Stmt {
// Assume new methods require no extra features
writeln!(f, " #[cfg(feature = \"{feature}\")]")?;
}
+ write!(f, "{unavailable}")?;
writeln!(
f,
"impl{} DefaultId for {}{} {{",
@@ -1452,7 +1493,7 @@ impl fmt::Display for Stmt {
cls,
generics,
protocol,
- availability: _,
+ availability,
} => {
let (generic_bound, where_bound) = if !generics.is_empty() {
match (&*protocol.library, &*protocol.name) {
@@ -1496,6 +1537,8 @@ impl fmt::Display for Stmt {
if let Some(feature) = cls.feature() {
writeln!(f, "#[cfg(feature = \"{feature}\")]")?;
}
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(
f,
"unsafe impl{} {} for {}{} {}{{}}",
@@ -1512,8 +1555,9 @@ impl fmt::Display for Stmt {
protocols,
methods,
} => {
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "extern_protocol!(")?;
- write!(f, "{availability}")?;
write!(f, " pub unsafe trait {}", id.name)?;
if !protocols.is_empty() {
@@ -1560,6 +1604,7 @@ impl fmt::Display for Stmt {
)?;
}
}
+ write!(f, "{unavailable}")?;
writeln!(f, "{method}")?;
}
writeln!(f, " }}")?;
@@ -1574,11 +1619,12 @@ impl fmt::Display for Stmt {
boxable: _,
fields,
} => {
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "extern_struct!(")?;
if let Some(encoding_name) = encoding_name {
writeln!(f, " #[encoding_name({encoding_name:?})]")?;
}
- write!(f, "{availability}")?;
writeln!(f, " pub struct {} {{", id.name)?;
for (name, ty) in fields {
write!(f, " ")?;
@@ -1606,9 +1652,10 @@ impl fmt::Display for Stmt {
Some(UnexposedAttr::ErrorEnum) => "ns_error_enum",
_ => panic!("invalid enum kind"),
};
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "{macro_name}!(")?;
writeln!(f, " #[underlying({ty})]")?;
- write!(f, "{availability}")?;
writeln!(
f,
" pub enum {} {{",
@@ -1623,18 +1670,22 @@ impl fmt::Display for Stmt {
}
Self::VarDecl {
id,
- availability: _,
+ availability,
ty,
value: None,
} => {
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "extern_static!({}: {ty});", id.name)?;
}
Self::VarDecl {
id,
- availability: _,
+ availability,
ty,
value: Some(expr),
} => {
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "extern_static!({}: {ty} = {expr});", id.name)?;
}
Self::FnDecl {
@@ -1700,20 +1751,26 @@ impl fmt::Display for Stmt {
}
Self::AliasDecl {
id,
- availability: _,
+ availability,
ty,
kind,
} => {
match kind {
Some(UnexposedAttr::TypedEnum) => {
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "typed_enum!(pub type {} = {ty};);", id.name)?;
}
Some(UnexposedAttr::TypedExtensibleEnum) => {
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "typed_extensible_enum!(pub type {} = {ty};);", id.name)?;
}
None | Some(UnexposedAttr::BridgedTypedef) => {
// "bridged" typedefs should just use a normal type
// alias.
+ let unavailable = availability.unavailable.clone();
+ write!(f, "{unavailable}")?;
writeln!(f, "pub type {} = {ty};", id.name)?;
}
kind => panic!("invalid alias kind {kind:?} for {ty:?}"),
diff --git a/crates/header-translator/translation-config.toml b/crates/header-translator/translation-config.toml
index 97ca20d14..9ae5a55e8 100644
--- a/crates/header-translator/translation-config.toml
+++ b/crates/header-translator/translation-config.toml
@@ -1532,6 +1532,12 @@ skipped = true
[class.MXMetricManager.methods.makeLogHandleWithCategory]
skipped = true
+# The MPMediaQueryAddition definition does not have API_UNAVAILABLE annotations
+# where as the MPMediaItem does. This results in problematic conditional compliation:
+# https://github.com/phracker/MacOSX-SDKs/blob/041600eda65c6a668f66cb7d56b7d1da3e8bcc93/MacOSX11.3.sdk/System/Library/Frameworks/MediaPlayer.framework/Versions/A/Headers/MPMediaQuery.h#L103-L118
+[class.MPMediaItem.categories.MPMediaQueryAdditions]
+skipped = true
+
# Custom generics because of auto traits
[class.NSArray]
definition-skipped = true
diff --git a/crates/icrate/src/generated b/crates/icrate/src/generated
index 13af2aa6e..d23e6b749 160000
--- a/crates/icrate/src/generated
+++ b/crates/icrate/src/generated
@@ -1 +1 @@
-Subproject commit 13af2aa6e6ebb964578d928468a46e4036464d52
+Subproject commit d23e6b749470c0dfdf8722ccc6d70d52cca7dc7d