Skip to content

Commit 94d8ac1

Browse files
authored
Merge pull request #493 from madsmtm/generate-framework-boilerplate
Generate more framework boilerplate automatically
2 parents e32b072 + 6d4d2d5 commit 94d8ac1

File tree

125 files changed

+382
-749
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+382
-749
lines changed

crates/header-translator/src/config.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ pub struct LibraryData {
6666
#[serde(default)]
6767
pub name: Option<String>,
6868
pub imports: Vec<String>,
69+
#[serde(rename = "gnustep-library")]
70+
#[serde(default)]
71+
pub gnustep_library: Option<String>,
72+
#[serde(default)]
73+
#[serde(rename = "extra-docs")]
74+
pub extra_docs: String,
75+
#[serde(default)]
76+
#[serde(rename = "additions")]
77+
pub has_additions: bool,
78+
#[serde(default)]
79+
#[serde(rename = "fixes")]
80+
pub has_fixes: bool,
6981
#[serde(rename = "extra-features")]
7082
#[serde(default)]
7183
pub extra_features: Vec<String>,
@@ -79,6 +91,16 @@ pub struct LibraryData {
7991
pub tvos: Option<semver::VersionReq>,
8092
#[serde(default)]
8193
pub watchos: Option<semver::VersionReq>,
94+
#[serde(default)]
95+
pub examples: Vec<Example>,
96+
}
97+
98+
#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]
99+
#[serde(deny_unknown_fields)]
100+
pub struct Example {
101+
pub name: String,
102+
#[serde(default)]
103+
pub description: String,
82104
}
83105

84106
#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]

crates/header-translator/src/file.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ use std::fmt;
33
use crate::context::Context;
44
use crate::stmt::Stmt;
55

6-
pub(crate) const FILE_PRELUDE: &str = r#"//! This file has been automatically generated by `objc2`'s `header-translator`.
7-
//! DO NOT EDIT"#;
8-
96
#[derive(Debug, PartialEq)]
107
pub struct File {
118
library_name: String,
@@ -41,7 +38,11 @@ impl File {
4138

4239
impl fmt::Display for File {
4340
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44-
writeln!(f, "{FILE_PRELUDE}")?;
41+
writeln!(
42+
f,
43+
"//! This file has been automatically generated by `objc2`'s `header-translator`."
44+
)?;
45+
writeln!(f, "//! DO NOT EDIT")?;
4546

4647
writeln!(f, "use crate::common::*;")?;
4748
writeln!(f, "use crate::{}::*;", self.library_name)?;

crates/header-translator/src/library.rs

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ use std::fs;
44
use std::io;
55
use std::path::Path;
66

7-
use crate::file::{File, FILE_PRELUDE};
7+
use crate::config::LibraryData;
8+
use crate::file::File;
89

910
#[derive(Debug, PartialEq, Default)]
1011
pub struct Library {
1112
pub files: BTreeMap<String, File>,
13+
link_name: String,
14+
data: LibraryData,
1215
}
1316

1417
impl Library {
15-
pub fn new() -> Self {
18+
pub fn new(name: &str, data: &LibraryData) -> Self {
1619
Self {
1720
files: BTreeMap::new(),
21+
link_name: name.to_string(),
22+
data: data.clone(),
1823
}
1924
}
2025

@@ -41,11 +46,81 @@ impl Library {
4146
}
4247
}
4348

49+
fn prepare_for_docs(s: &str) -> String {
50+
s.trim().replace('\n', "\n//! ")
51+
}
52+
4453
impl fmt::Display for Library {
4554
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46-
writeln!(f, "{FILE_PRELUDE}")?;
47-
writeln!(f, "#![allow(unused_imports)]")?;
48-
writeln!(f, "#![allow(deprecated)]")?;
55+
let name = self.data.name.as_deref().unwrap_or(&self.link_name);
56+
writeln!(
57+
f,
58+
"// This file has been automatically generated by `objc2`'s `header-translator`."
59+
)?;
60+
writeln!(f, "// DO NOT EDIT")?;
61+
writeln!(f)?;
62+
writeln!(f, "//! # Bindings to the `{name}` framework")?;
63+
if !self.data.extra_docs.is_empty() {
64+
writeln!(f, "//!")?;
65+
writeln!(f, "//! {}.", prepare_for_docs(&self.data.extra_docs))?;
66+
}
67+
if !self.data.examples.is_empty() {
68+
writeln!(f, "//!")?;
69+
writeln!(f, "//!")?;
70+
let examples_plural = if self.data.examples.len() > 1 {
71+
"s"
72+
} else {
73+
""
74+
};
75+
writeln!(f, "//! ## Example{examples_plural}")?;
76+
for example in &self.data.examples {
77+
writeln!(f, "//!")?;
78+
writeln!(f, "//! {}.", prepare_for_docs(&example.description))?;
79+
writeln!(f, "//!")?;
80+
writeln!(f, "//! ```ignore")?;
81+
writeln!(
82+
f,
83+
"#![doc = include_str!(\"../../../examples/{}.rs\")]",
84+
example.name
85+
)?;
86+
writeln!(f, "//! ```")?;
87+
}
88+
}
89+
if self.data.name.is_some() {
90+
writeln!(f, "#![doc(alias = \"{}\")]", self.link_name)?;
91+
}
92+
writeln!(f)?;
93+
94+
if self.data.has_additions {
95+
writeln!(f, "#[path = \"../../additions/{name}/mod.rs\"]")?;
96+
writeln!(f, "mod additions;")?;
97+
writeln!(f, "pub use self::additions::*;")?;
98+
}
99+
writeln!(f)?;
100+
if self.data.has_fixes {
101+
writeln!(f, "#[path = \"../../fixes/{name}/mod.rs\"]")?;
102+
writeln!(f, "mod fixes;")?;
103+
writeln!(f, "pub use self::fixes::*;")?;
104+
}
105+
writeln!(f)?;
106+
107+
// Link to the correct framework
108+
//
109+
// FIXME: We always do cfg_attr(feature = "apple", ...) to make compiling things for GNUStep easier.
110+
writeln!(
111+
f,
112+
"#[cfg_attr(feature = \"apple\", link(name = \"{}\", kind = \"framework\"))]",
113+
self.link_name
114+
)?;
115+
if let Some(gnustep_library) = &self.data.gnustep_library {
116+
writeln!(
117+
f,
118+
"#[cfg_attr(feature = \"gnustep-1-7\", link(name = \"{}\", kind = \"dylib\"))]",
119+
gnustep_library
120+
)?;
121+
}
122+
writeln!(f, "extern \"C\" {{}}")?;
123+
writeln!(f)?;
49124

50125
for name in self.files.keys() {
51126
// NOTE: some SDK files have '+' in the file name

crates/header-translator/src/main.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::fs;
12
use std::io::{Read, Write};
23
use std::path::{Path, PathBuf};
34

@@ -138,19 +139,26 @@ fn main() -> Result<(), BoxError> {
138139
cache.update(&mut final_result);
139140
drop(span);
140141

142+
let generated_dir = crate_src.join("generated");
143+
fs::create_dir_all(&generated_dir)?;
144+
141145
for (library_name, files) in &final_result.libraries {
142146
let _span = info_span!("writing", library_name).entered();
143-
let output_path = crate_src.join("generated").join(library_name);
144-
std::fs::create_dir_all(&output_path)?;
147+
let output_path = generated_dir.join(library_name);
148+
fs::create_dir_all(&output_path)?;
145149
files.output(&output_path).unwrap();
146150
}
147151

152+
final_result
153+
.output_module(&generated_dir.join("mod.rs"))
154+
.unwrap();
155+
148156
let span = info_span!("writing features").entered();
149157
const FEATURE_SECTION_PATTERN:
150158
&str = "# This section has been automatically generated by `objc2`'s `header-translator`.\n# DO NOT EDIT\n";
151159
let mut cargo_toml = {
152160
let path = crate_src.parent().unwrap().join("Cargo.toml");
153-
std::fs::OpenOptions::new()
161+
fs::OpenOptions::new()
154162
.read(true)
155163
.write(true)
156164
.append(true)
@@ -197,7 +205,7 @@ fn parse_sdk(index: &Index<'_>, sdk: &SdkPath, llvm_target: &str, config: &Confi
197205
let tu = get_translation_unit(index, sdk, llvm_target);
198206

199207
let mut preprocessing = true;
200-
let mut result = Output::from_libraries(config.libraries.keys());
208+
let mut result = Output::from_libraries(&config.libraries);
201209

202210
let mut library_span = None;
203211
let mut library_span_name = String::new();

crates/header-translator/src/output.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
use std::collections::{BTreeMap, BTreeSet};
1+
use std::collections::{BTreeMap, BTreeSet, HashMap};
2+
use std::fmt::{self, Write};
3+
use std::fs;
4+
use std::path::Path;
25
use std::str::FromStr;
36

4-
use crate::config::Config;
7+
use crate::config::{Config, LibraryData};
58
use crate::library::Library;
69
use crate::stmt::Stmt;
710

@@ -11,10 +14,10 @@ pub struct Output {
1114
}
1215

1316
impl Output {
14-
pub fn from_libraries(libraries: impl IntoIterator<Item = impl Into<String>>) -> Self {
17+
pub fn from_libraries(libraries: &HashMap<String, LibraryData>) -> Self {
1518
let libraries = libraries
16-
.into_iter()
17-
.map(|name| (name.into(), Library::new()))
19+
.iter()
20+
.map(|(name, data)| (name.into(), Library::new(name, data)))
1821
.collect();
1922
Self { libraries }
2023
}
@@ -30,6 +33,19 @@ impl Output {
3033
);
3134
}
3235

36+
pub fn output_module(&self, path: &Path) -> fmt::Result {
37+
let mut f = String::new();
38+
39+
for library_name in self.libraries.keys() {
40+
writeln!(&mut f, "#[cfg(feature = \"{library_name}\")]")?;
41+
writeln!(&mut f, "pub mod {library_name};")?;
42+
}
43+
44+
fs::write(path, f).unwrap();
45+
46+
Ok(())
47+
}
48+
3349
pub fn cargo_features(&self, config: &Config) -> BTreeMap<String, Vec<String>> {
3450
let mut features = BTreeMap::new();
3551

@@ -58,6 +74,7 @@ impl Output {
5874
]
5975
.into_iter()
6076
.collect();
77+
let mut gnustep_features: BTreeSet<String> = vec![].into_iter().collect();
6178

6279
for (mut library_name, library) in &config.libraries {
6380
if let Some(alias) = &library.name {
@@ -83,6 +100,10 @@ impl Output {
83100
macos_13_features.insert(format!("{library_name}_all"));
84101
}
85102
}
103+
104+
if library.gnustep_library.is_some() {
105+
gnustep_features.insert(format!("{library_name}_all"));
106+
}
86107
}
87108

88109
let _ = features.insert(
@@ -105,6 +126,10 @@ impl Output {
105126
"unstable-frameworks-macos-13".into(),
106127
macos_13_features.into_iter().collect(),
107128
);
129+
let _ = features.insert(
130+
"unstable-frameworks-gnustep".into(),
131+
gnustep_features.into_iter().collect(),
132+
);
108133

109134
for (library_name, library) in &self.libraries {
110135
let library_alias = config.get_library_alias(library_name.clone());

0 commit comments

Comments
 (0)