Skip to content

Commit

Permalink
Merge pull request #493 from madsmtm/generate-framework-boilerplate
Browse files Browse the repository at this point in the history
Generate more framework boilerplate automatically
  • Loading branch information
madsmtm authored Sep 1, 2023
2 parents e32b072 + 6d4d2d5 commit 94d8ac1
Show file tree
Hide file tree
Showing 125 changed files with 382 additions and 749 deletions.
22 changes: 22 additions & 0 deletions crates/header-translator/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ pub struct LibraryData {
#[serde(default)]
pub name: Option<String>,
pub imports: Vec<String>,
#[serde(rename = "gnustep-library")]
#[serde(default)]
pub gnustep_library: Option<String>,
#[serde(default)]
#[serde(rename = "extra-docs")]
pub extra_docs: String,
#[serde(default)]
#[serde(rename = "additions")]
pub has_additions: bool,
#[serde(default)]
#[serde(rename = "fixes")]
pub has_fixes: bool,
#[serde(rename = "extra-features")]
#[serde(default)]
pub extra_features: Vec<String>,
Expand All @@ -79,6 +91,16 @@ pub struct LibraryData {
pub tvos: Option<semver::VersionReq>,
#[serde(default)]
pub watchos: Option<semver::VersionReq>,
#[serde(default)]
pub examples: Vec<Example>,
}

#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub struct Example {
pub name: String,
#[serde(default)]
pub description: String,
}

#[derive(Deserialize, Debug, Default, Clone, PartialEq, Eq)]
Expand Down
9 changes: 5 additions & 4 deletions crates/header-translator/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ use std::fmt;
use crate::context::Context;
use crate::stmt::Stmt;

pub(crate) const FILE_PRELUDE: &str = r#"//! This file has been automatically generated by `objc2`'s `header-translator`.
//! DO NOT EDIT"#;

#[derive(Debug, PartialEq)]
pub struct File {
library_name: String,
Expand Down Expand Up @@ -41,7 +38,11 @@ impl File {

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

writeln!(f, "use crate::common::*;")?;
writeln!(f, "use crate::{}::*;", self.library_name)?;
Expand Down
85 changes: 80 additions & 5 deletions crates/header-translator/src/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ use std::fs;
use std::io;
use std::path::Path;

use crate::file::{File, FILE_PRELUDE};
use crate::config::LibraryData;
use crate::file::File;

#[derive(Debug, PartialEq, Default)]
pub struct Library {
pub files: BTreeMap<String, File>,
link_name: String,
data: LibraryData,
}

impl Library {
pub fn new() -> Self {
pub fn new(name: &str, data: &LibraryData) -> Self {
Self {
files: BTreeMap::new(),
link_name: name.to_string(),
data: data.clone(),
}
}

Expand All @@ -41,11 +46,81 @@ impl Library {
}
}

fn prepare_for_docs(s: &str) -> String {
s.trim().replace('\n', "\n//! ")
}

impl fmt::Display for Library {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{FILE_PRELUDE}")?;
writeln!(f, "#![allow(unused_imports)]")?;
writeln!(f, "#![allow(deprecated)]")?;
let name = self.data.name.as_deref().unwrap_or(&self.link_name);
writeln!(
f,
"// This file has been automatically generated by `objc2`'s `header-translator`."
)?;
writeln!(f, "// DO NOT EDIT")?;
writeln!(f)?;
writeln!(f, "//! # Bindings to the `{name}` framework")?;
if !self.data.extra_docs.is_empty() {
writeln!(f, "//!")?;
writeln!(f, "//! {}.", prepare_for_docs(&self.data.extra_docs))?;
}
if !self.data.examples.is_empty() {
writeln!(f, "//!")?;
writeln!(f, "//!")?;
let examples_plural = if self.data.examples.len() > 1 {
"s"
} else {
""
};
writeln!(f, "//! ## Example{examples_plural}")?;
for example in &self.data.examples {
writeln!(f, "//!")?;
writeln!(f, "//! {}.", prepare_for_docs(&example.description))?;
writeln!(f, "//!")?;
writeln!(f, "//! ```ignore")?;
writeln!(
f,
"#![doc = include_str!(\"../../../examples/{}.rs\")]",
example.name
)?;
writeln!(f, "//! ```")?;
}
}
if self.data.name.is_some() {
writeln!(f, "#![doc(alias = \"{}\")]", self.link_name)?;
}
writeln!(f)?;

if self.data.has_additions {
writeln!(f, "#[path = \"../../additions/{name}/mod.rs\"]")?;
writeln!(f, "mod additions;")?;
writeln!(f, "pub use self::additions::*;")?;
}
writeln!(f)?;
if self.data.has_fixes {
writeln!(f, "#[path = \"../../fixes/{name}/mod.rs\"]")?;
writeln!(f, "mod fixes;")?;
writeln!(f, "pub use self::fixes::*;")?;
}
writeln!(f)?;

// Link to the correct framework
//
// FIXME: We always do cfg_attr(feature = "apple", ...) to make compiling things for GNUStep easier.
writeln!(
f,
"#[cfg_attr(feature = \"apple\", link(name = \"{}\", kind = \"framework\"))]",
self.link_name
)?;
if let Some(gnustep_library) = &self.data.gnustep_library {
writeln!(
f,
"#[cfg_attr(feature = \"gnustep-1-7\", link(name = \"{}\", kind = \"dylib\"))]",
gnustep_library
)?;
}
writeln!(f, "extern \"C\" {{}}")?;
writeln!(f)?;

for name in self.files.keys() {
// NOTE: some SDK files have '+' in the file name
Expand Down
16 changes: 12 additions & 4 deletions crates/header-translator/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::fs;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

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

let generated_dir = crate_src.join("generated");
fs::create_dir_all(&generated_dir)?;

for (library_name, files) in &final_result.libraries {
let _span = info_span!("writing", library_name).entered();
let output_path = crate_src.join("generated").join(library_name);
std::fs::create_dir_all(&output_path)?;
let output_path = generated_dir.join(library_name);
fs::create_dir_all(&output_path)?;
files.output(&output_path).unwrap();
}

final_result
.output_module(&generated_dir.join("mod.rs"))
.unwrap();

let span = info_span!("writing features").entered();
const FEATURE_SECTION_PATTERN:
&str = "# This section has been automatically generated by `objc2`'s `header-translator`.\n# DO NOT EDIT\n";
let mut cargo_toml = {
let path = crate_src.parent().unwrap().join("Cargo.toml");
std::fs::OpenOptions::new()
fs::OpenOptions::new()
.read(true)
.write(true)
.append(true)
Expand Down Expand Up @@ -197,7 +205,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();
Expand Down
35 changes: 30 additions & 5 deletions crates/header-translator/src/output.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::collections::{BTreeMap, BTreeSet};
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::fmt::{self, Write};
use std::fs;
use std::path::Path;
use std::str::FromStr;

use crate::config::Config;
use crate::config::{Config, LibraryData};
use crate::library::Library;
use crate::stmt::Stmt;

Expand All @@ -11,10 +14,10 @@ pub struct Output {
}

impl Output {
pub fn from_libraries(libraries: impl IntoIterator<Item = impl Into<String>>) -> Self {
pub fn from_libraries(libraries: &HashMap<String, LibraryData>) -> Self {
let libraries = libraries
.into_iter()
.map(|name| (name.into(), Library::new()))
.iter()
.map(|(name, data)| (name.into(), Library::new(name, data)))
.collect();
Self { libraries }
}
Expand All @@ -30,6 +33,19 @@ impl Output {
);
}

pub fn output_module(&self, path: &Path) -> fmt::Result {
let mut f = String::new();

for library_name in self.libraries.keys() {
writeln!(&mut f, "#[cfg(feature = \"{library_name}\")]")?;
writeln!(&mut f, "pub mod {library_name};")?;
}

fs::write(path, f).unwrap();

Ok(())
}

pub fn cargo_features(&self, config: &Config) -> BTreeMap<String, Vec<String>> {
let mut features = BTreeMap::new();

Expand Down Expand Up @@ -58,6 +74,7 @@ impl Output {
]
.into_iter()
.collect();
let mut gnustep_features: BTreeSet<String> = vec![].into_iter().collect();

for (mut library_name, library) in &config.libraries {
if let Some(alias) = &library.name {
Expand All @@ -83,6 +100,10 @@ impl Output {
macos_13_features.insert(format!("{library_name}_all"));
}
}

if library.gnustep_library.is_some() {
gnustep_features.insert(format!("{library_name}_all"));
}
}

let _ = features.insert(
Expand All @@ -105,6 +126,10 @@ impl Output {
"unstable-frameworks-macos-13".into(),
macos_13_features.into_iter().collect(),
);
let _ = features.insert(
"unstable-frameworks-gnustep".into(),
gnustep_features.into_iter().collect(),
);

for (library_name, library) in &self.libraries {
let library_alias = config.get_library_alias(library_name.clone());
Expand Down
Loading

0 comments on commit 94d8ac1

Please sign in to comment.