diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 00000000..dbfa4063
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,3 @@
+
+[target.aarch64-apple-ios]
+runner = "cargo-box runner"
diff --git a/.gitignore b/.gitignore
index e71062d6..bede7500 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,6 @@
-/target
-
-
-# Added by cargo
-#
-# already existing elements were commented out
-
xcuserdata
/target
.DS_Store
+.box
Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
index 5c71ff88..fd8844b5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,6 +6,10 @@ members = [
"cidre-macros",
]
+exclude = [
+ "cargo-box"
+]
+
# [profile.dev]
# opt-level = 1
diff --git a/README.md b/README.md
index 094b3a54..eed6d590 100644
--- a/README.md
+++ b/README.md
@@ -23,9 +23,25 @@
- [x] macOS
- [x] iOS/iPadOS
- [x] tvOS
-- [x] watchOS
+- [ ] watchOS
- [ ] visionOS
+### iOS devices runner
+
+Run tests on your iPhone or iPad.
+
+1. Run `cargo install --path ./cargo-box` to install cargo box plugin
+2. Run `cargo box teams` to find out your DEVELOMPENT_TEAM id
+3. Run `cargo box devices` to find out your DEVICE_ID
+4. Create `.box` file with contents:
+```
+BOX_ORG_ID = unique org id, for instance org.cidre
+DEVELOPMENT_TEAM = team id from steep 2
+DEVICE_ID = device id from step 3
+```
+5. Run `cargo t --target aarch64-apple-ios`
+6. Run `cargo r --target aarch64-apple-ios --example`
+
### Versioning (API Availability)
Deployment targets are controlled via features `macos_x_x`, `ios_x_x`, `tvos_x_x`, `watchos_x_x`, `visionos_x_x`.
diff --git a/build.sh b/build.sh
index 623239f7..5a54a5e5 100755
--- a/build.sh
+++ b/build.sh
@@ -5,6 +5,7 @@ cargo b --target aarch64-apple-ios
cargo b --target aarch64-apple-ios-sim
cargo +nightly b -Zbuild-std --target aarch64-apple-tvos
cargo +nightly b -Zbuild-std --target aarch64-apple-tvos-sim
+# cargo +nightly b -Zbuild-std --target arm64_32-apple-watchos
cargo b --target aarch64-apple-darwin
cargo b --target x86_64-apple-darwin
# cargo +nightly b -Zbuild-std --target aarch64-apple-visionos
diff --git a/cargo-box/.gitignore b/cargo-box/.gitignore
new file mode 100644
index 00000000..ea8c4bf7
--- /dev/null
+++ b/cargo-box/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/cargo-box/Cargo.toml b/cargo-box/Cargo.toml
new file mode 100644
index 00000000..be29576a
--- /dev/null
+++ b/cargo-box/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "cargo-box"
+version = "0.1.0"
+edition = "2021"
+description = "iOS runner"
+license = "MIT"
+
+[dependencies]
+clap = { version = "4.5", features = ["derive"] }
+serde = { version = "1.0.215", features = ["derive"] }
+serde_json = "1.0.133"
+cargo_toml = "0.21"
+dotenv = "0.15.0"
+
+cidre = { path = "../cidre", default-features = false, features = ["ns", "cg", "cf", "sec"] }
diff --git a/cargo-box/box/box.entitlements b/cargo-box/box/box.entitlements
new file mode 100644
index 00000000..18aff0ce
--- /dev/null
+++ b/cargo-box/box/box.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.files.user-selected.read-only
+
+
+
diff --git a/cargo-box/box/box.xcodeproj/project.pbxproj b/cargo-box/box/box.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..7b09c2fd
--- /dev/null
+++ b/cargo-box/box/box.xcodeproj/project.pbxproj
@@ -0,0 +1,190 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 77;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ D2B940072D12B915004F24BF /* bin in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2B940052D12B900004F24BF /* bin */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ D2B93FFB2D11B5E0004F24BF /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 6;
+ files = (
+ D2B940072D12B915004F24BF /* bin in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ D2798B842CFCC59D00A4ABEF /* cfg.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = cfg.xcconfig; sourceTree = ""; };
+ D28FC8DE2CFCB6E200FE81B8 /* box.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = box.entitlements; sourceTree = ""; };
+ D2B940052D12B900004F24BF /* bin */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = bin; path = "$(PRODUCT_NAME)"; sourceTree = ""; };
+ D2BD41F22CFC8A5D000CF128 /* box.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = box.app; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ D2BD41EF2CFC8A5D000CF128 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ D2BD41E92CFC8A5D000CF128 = {
+ isa = PBXGroup;
+ children = (
+ D2798B842CFCC59D00A4ABEF /* cfg.xcconfig */,
+ D28FC8DE2CFCB6E200FE81B8 /* box.entitlements */,
+ D2B940052D12B900004F24BF /* bin */,
+ D2BD41F32CFC8A5D000CF128 /* Products */,
+ );
+ sourceTree = "";
+ };
+ D2BD41F32CFC8A5D000CF128 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ D2BD41F22CFC8A5D000CF128 /* box.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ D2BD41F12CFC8A5D000CF128 /* box */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = D2BD42012CFC8A5E000CF128 /* Build configuration list for PBXNativeTarget "box" */;
+ buildPhases = (
+ D2BD41EE2CFC8A5D000CF128 /* Sources */,
+ D2BD41EF2CFC8A5D000CF128 /* Frameworks */,
+ D2BD41F02CFC8A5D000CF128 /* Resources */,
+ D2B93FFB2D11B5E0004F24BF /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = box;
+ packageProductDependencies = (
+ );
+ productName = box;
+ productReference = D2BD41F22CFC8A5D000CF128 /* box.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ D2BD41EA2CFC8A5D000CF128 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1620;
+ LastUpgradeCheck = 1620;
+ TargetAttributes = {
+ D2BD41F12CFC8A5D000CF128 = {
+ CreatedOnToolsVersion = 16.2;
+ };
+ };
+ };
+ buildConfigurationList = D2BD41ED2CFC8A5D000CF128 /* Build configuration list for PBXProject "box" */;
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = D2BD41E92CFC8A5D000CF128;
+ minimizedProjectReferenceProxies = 1;
+ preferredProjectObjectVersion = 77;
+ productRefGroup = D2BD41F32CFC8A5D000CF128 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ D2BD41F12CFC8A5D000CF128 /* box */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ D2BD41F02CFC8A5D000CF128 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ D2BD41EE2CFC8A5D000CF128 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ D2BD41FF2CFC8A5E000CF128 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D2798B842CFCC59D00A4ABEF /* cfg.xcconfig */;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ D2BD42002CFC8A5E000CF128 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = D2798B842CFCC59D00A4ABEF /* cfg.xcconfig */;
+ buildSettings = {
+ };
+ name = Release;
+ };
+ D2BD42022CFC8A5E000CF128 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Debug;
+ };
+ D2BD42032CFC8A5E000CF128 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ D2BD41ED2CFC8A5D000CF128 /* Build configuration list for PBXProject "box" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D2BD41FF2CFC8A5E000CF128 /* Debug */,
+ D2BD42002CFC8A5E000CF128 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ D2BD42012CFC8A5E000CF128 /* Build configuration list for PBXNativeTarget "box" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ D2BD42022CFC8A5E000CF128 /* Debug */,
+ D2BD42032CFC8A5E000CF128 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = D2BD41EA2CFC8A5D000CF128 /* Project object */;
+}
diff --git a/cargo-box/box/box.xcodeproj/xcshareddata/xcschemes/box.xcscheme b/cargo-box/box/box.xcodeproj/xcshareddata/xcschemes/box.xcscheme
new file mode 100644
index 00000000..457eade0
--- /dev/null
+++ b/cargo-box/box/box.xcodeproj/xcshareddata/xcschemes/box.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cargo-box/box/cfg.xcconfig b/cargo-box/box/cfg.xcconfig
new file mode 100644
index 00000000..43945e5d
--- /dev/null
+++ b/cargo-box/box/cfg.xcconfig
@@ -0,0 +1,49 @@
+//
+// cfg.xcconfig
+
+// Configuration settings file format documentation can be found at:
+// https://help.apple.com/xcode/#/dev745c5c974
+// https://developer.apple.com/documentation/xcode/adding-a-build-configuration-file-to-your-project
+
+EXCLUDED_ARCHS[sdk=macos*] = x86_64
+
+ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES
+SDKROOT = auto
+SUPPORTED_PLATFORMS = xrsimulator xros watchsimulator watchos macosx iphonesimulator iphoneos appletvsimulator appletvos
+SUPPORTS_MACCATALYST = YES
+
+CODE_SIGN_ENTITLEMENTS = box.entitlements
+PROVISIONING_PROFILE_SPECIFIER =
+CODE_SIGN_IDENTITY = Apple Development
+CODE_SIGN_STYLE = Automatic
+ENABLE_HARDENED_RUNTIME = YES
+GENERATE_INFOPLIST_FILE = YES
+LD_RUNPATH_SEARCH_PATHS = @executable_path/Frameworks
+LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = @executable_path/../Frameworks
+TARGETED_DEVICE_FAMILY = 1,2,7
+
+ALWAYS_SEARCH_USER_PATHS = NO
+
+MARKETING_VERSION = 1.0.0
+CURRENT_PROJECT_VERSION = 1
+
+PRODUCT_NAME = box
+PRODUCT_DISPLAY_NAME = $(PRODUCT_NAME)
+
+
+INFOPLIST_KEY_NSCameraUsageDescription = Testing Camera
+
+BOX_ID = box
+BOX_ORG_ID = org.cidre
+
+// Anjlab
+//DEVELOPMENT_TEAM = FD6ZML48V9
+BOX_ORG_ID = org.cidre
+
+
+// Custom box overrides generated by cargo-bx
+#include? "box.xcconfig"
+
+PRODUCT_BUNDLE_IDENTIFIER = $(BOX_ORG_ID).$(BOX_ID)
+INFOPLIST_KEY_CFBundleDisplayName = $(PRODUCT_NAME)
+BOX_BIN_PATH = $(PRODUCT_NAME)
diff --git a/cargo-box/src/main.rs b/cargo-box/src/main.rs
new file mode 100644
index 00000000..5bc466b9
--- /dev/null
+++ b/cargo-box/src/main.rs
@@ -0,0 +1,708 @@
+use clap::{Parser, Subcommand};
+use std::env;
+
+#[derive(Parser, Debug)]
+#[command(version, about, long_about = None)]
+struct Cli {
+ #[command(subcommand)]
+ cmd: Cmd,
+}
+
+#[derive(Subcommand, Debug)]
+enum Cmd {
+ /// Used via cargo.toml runner
+ #[command()]
+ Runner(runner::Args),
+
+ /// List dev teams on this mac
+ #[command()]
+ Teams,
+
+ /// List connected devices on this mac
+ #[command()]
+ Devices,
+
+ /// Create configured xcode project for binary, test or example
+ /// in target/boxes for runner.
+ #[command()]
+ Proj(xcode::ProjArgs),
+}
+
+fn main() {
+ // Handle different args from different calls:
+ //
+ // cargo-box -> cargo-bx (no extra box)
+ // cargo box -> cargo-bx bx (extra box)
+ let mut args: Vec<_> = env::args().collect();
+ if args.get(1).map(|v| v == "box") == Some(true) {
+ args.remove(1);
+ }
+
+ // special case for runner to skip Cli::parse_from
+ if args.get(1).map(|v| v == "runner") == Some(true) {
+ return runner::run(runner::Args { args });
+ }
+
+ match Cli::parse_from(args).cmd {
+ Cmd::Teams => teams::list(),
+ Cmd::Devices => device_ctl::list_devices(),
+ Cmd::Proj(args) => xcode::proj(args),
+ _ => panic!("unknown command"),
+ }
+}
+
+mod runner {
+ use std::{
+ env,
+ path::{Path, PathBuf},
+ };
+
+ use clap::Parser;
+
+ use crate::macos::{device_ctl, xcode};
+
+ #[derive(Parser, Debug)]
+ pub(crate) struct Args {
+ /// Arguments for the runner
+ #[arg()]
+ pub(crate) args: Vec,
+ }
+
+ pub(crate) fn run(args: Args) {
+ // println!("{args:?}");
+ assert!(args.args.len() > 2);
+ let binary = &args.args[2];
+ let path = Path::new(binary);
+
+ let err = "can't parse filename";
+
+ let Some(name) = path.file_name() else {
+ panic!("{err}");
+ };
+
+ let Some(name) = name.to_str() else {
+ panic!("{err}");
+ };
+
+ let mut name = name.to_string();
+
+ let Some(parent) = path.parent() else {
+ panic!("{err}");
+ };
+
+ let Some(folder) = parent.file_name() else {
+ panic!("{err}");
+ };
+
+ let Some(folder) = folder.to_str() else {
+ panic!("{err}");
+ };
+ let mut is_example = false;
+ let mut is_dep = false;
+ let mut is_binary = false;
+ let parent = match folder {
+ "examples" => {
+ // target/[optional-tripple/]profile|/examples/example
+ is_example = true;
+ parent.parent()
+ }
+ "deps" => {
+ // [fullpath/]target/[optional-tripple/]profile|/deps/test-or-bench
+ is_dep = true;
+ parent.parent()
+ }
+ "debug" => {
+ // target/[optional-tripple/]profile|/binary
+ // binary
+ is_binary = true;
+ Some(parent)
+ }
+ "release" => {
+ // target/[optional-tripple/]profile|/binary
+ // binary
+ is_binary = true;
+ Some(parent)
+ }
+ _ => panic!("{err}"),
+ };
+
+ let Some(parent) = parent else {
+ panic!("{err}");
+ };
+ let Some(folder) = parent.file_name() else {
+ panic!("{err}");
+ };
+ let Some(folder) = folder.to_str() else {
+ panic!("{err}");
+ };
+
+ let config = match folder {
+ "debug" => "Debug",
+ "release" => "Release",
+ _ => panic!("{err}"),
+ };
+
+ let Some(mut parent) = parent.parent() else {
+ panic!("{err}");
+ };
+ let Some(folder) = parent.file_name() else {
+ panic!("{err}");
+ };
+
+ let Some(folder) = folder.to_str() else {
+ panic!("{err}");
+ };
+
+ let sdk = match folder {
+ "target" | "aarch64-apple-darwin" | "x86_64-apple-darwin" => "macos",
+ "aarch64-apple-ios" => "iphoneos",
+ "x86_64-apple-ios" | "aarch64-apple-ios-sim" => "iphonesimulator",
+ "aarch64-apple-tvos" => "appletvos",
+ "aarch64-apple-tvos-sim" => "appletvsimulator",
+ "arm64_32-apple-watchos" => "watchos",
+ "aarch64-apple-watchos" => "watchos",
+ "aarch64-apple-watchos-sim" => "watchsimulator",
+ "aarch64-apple-visionos" => "xros",
+ "aarch64-apple-visionos-sim" => "xrsimulator",
+ _ => panic!("{err}"),
+ };
+
+ let platform = match sdk {
+ "macos" => "macOS",
+ "iphoneos" => "iOS",
+ "tvos" => "tvOS",
+ "watchos" => "watchOS",
+ "xros" => "visionOS",
+ "iphoneosimulator" => "iOS Simulator",
+ "appletvsimulator" => "tvOS Simulator",
+ "watchsimulator" => "watchOS Simulator",
+ "xrsimulator" => "visionOS Simulator",
+ x => panic!("unknown platform {x}"),
+ };
+
+ if folder != "target" {
+ parent = parent.parent().unwrap();
+ }
+
+ let Some(folder) = parent.file_name() else {
+ panic!("{err}");
+ };
+
+ if folder.to_os_string() == std::ffi::OsStr::new("target") {
+ parent = parent.parent().unwrap();
+ }
+
+ if !parent.as_os_str().is_empty() {
+ env::set_current_dir(parent).unwrap();
+ }
+
+ if is_dep {
+ // /Users/yury/Projects/cidre/target/aarch64-apple-ios/release/deps/uuid-3968b503a26aa57f
+ if let Some((name_part, _hash)) = name.rsplit_once('-') {
+ name = name_part.to_string();
+ }
+ }
+
+ let mut proj_args = xcode::ProjArgs {
+ bin: None,
+ example: None,
+ dep: None,
+ };
+
+ if is_example {
+ proj_args.example = Some(name.clone());
+ } else if is_binary {
+ proj_args.bin = Some(name.clone());
+ } else if is_dep {
+ proj_args.dep = Some(name.clone());
+ }
+
+ xcode::proj(proj_args);
+
+ let mut project = PathBuf::from("./target/boxes");
+ if is_example {
+ project.push("examples");
+ }
+ if is_dep {
+ project.push("deps");
+ }
+
+ let mut target = project.clone();
+ target.push(format!("build-{name}"));
+
+ project.push(&name);
+ project.push(&name);
+
+ // TODO: if is_binary, try replace with BOX_BIN_PATH instead of copy
+ std::fs::copy(binary, &project).unwrap();
+ project.pop();
+
+ project.push("box.xcodeproj");
+
+ xcode::build(
+ project.to_str().unwrap(),
+ platform,
+ config,
+ target.to_str().unwrap(),
+ );
+
+ target.push("Build");
+ target.push("Products");
+
+ target.push(&format!("{config}-{sdk}"));
+ target.push(&format!("{name}.app"));
+
+ let box_org_id = std::env::var("BOX_ORG_ID").unwrap();
+
+ let device_id = std::env::var("DEVICE_ID").unwrap();
+
+ device_ctl::install_app(&device_id, target.to_str().unwrap());
+ device_ctl::run_app(&device_id, &format!("{box_org_id}.{name}"), &args.args[3..]);
+ }
+}
+
+mod teams {
+ use cidre::{arc, cf, sec};
+
+ pub(crate) fn list() {
+ let query = cf::DictionaryOf::with_keys_values(
+ &[
+ sec::class_key(),
+ sec::match_keys::limit(),
+ sec::match_keys::policy(),
+ ],
+ &[
+ sec::class::certificate().as_type_ref(),
+ sec::match_limit::all(),
+ &sec::Policy::with_props(sec::Policy::apple_code_signing(), None).unwrap(),
+ ],
+ );
+
+ let certs = sec::item_matching(&query).unwrap();
+
+ assert_eq!(certs.get_type_id(), cf::Array::type_id());
+ let certs: arc::R> = unsafe { std::mem::transmute(certs) };
+
+ let mut filter_set = std::collections::HashSet::new();
+ let subject_key = sec::cert_oids::x509_v1_subject_name();
+ let org_name_label = sec::cert_oids::organization_name();
+ let unit_name_label = sec::cert_oids::organizational_unit_name();
+ let prop_value_key = sec::prop_keys::value();
+ let prop_label_key = sec::prop_keys::label();
+ let keys = cf::ArrayOf::from_slice(&[subject_key]);
+ for cert in certs.iter() {
+ let Ok(vals) = cert.values(&keys) else {
+ continue;
+ };
+ let Some(value) = vals.get(subject_key) else {
+ continue;
+ };
+ let Some(section) = value.get(prop_value_key) else {
+ continue;
+ };
+ assert_eq!(section.get_type_id(), cf::Array::type_id());
+
+ let section: &cf::ArrayOf> =
+ unsafe { std::mem::transmute(section) };
+
+ let mut team_id = None;
+ let mut team_name = None;
+ for dict in section.iter() {
+ let Some(label) = dict.get(prop_label_key) else {
+ continue;
+ };
+ let Some(value) = dict.get(prop_value_key) else {
+ continue;
+ };
+
+ // todo: build safer api
+ if value.get_type_id() != cf::String::type_id() {
+ continue;
+ }
+ let value: &cf::String = unsafe { std::mem::transmute(value) };
+
+ if label.equal(org_name_label) {
+ team_name = Some(value);
+ } else if label.equal(unit_name_label) {
+ team_id = Some(value);
+ }
+ }
+
+ if let (Some(id), Some(name)) = (team_id, team_name) {
+ if filter_set.contains(id) {
+ continue;
+ };
+ println!("{id}: {name}");
+ filter_set.insert(id);
+ }
+ }
+ if filter_set.is_empty() {
+ println!("no teams are found");
+ }
+ }
+}
+
+mod cargo {
+ use std::{env, path::PathBuf};
+
+ use cargo_toml::{Manifest, Workspace};
+
+ pub(crate) fn manifests() -> Option<(PathBuf, Vec, Option)> {
+ let mut path = root()?.join("Cargo.toml");
+ if let Ok(mut res) = Manifest::from_path(&path) {
+ path.pop();
+ res.complete_from_path(&path).unwrap();
+ if let Some(ws) = res.workspace {
+ let mut vec = Vec::with_capacity(ws.members.len());
+ for member in ws.members.iter() {
+ path = path.join(&format!("{member}/Cargo.toml"));
+ let mut man = Manifest::from_path(&path).unwrap();
+ path.pop();
+ man.complete_from_path(&path).unwrap();
+ path.pop();
+ vec.push(man);
+ }
+ Some((path, vec, Some(ws)))
+ } else {
+ // println!("workspace: {:?}", res.workspace);
+ Some((path, vec![res], None))
+ }
+ } else {
+ None
+ }
+ }
+
+ fn root() -> Option {
+ let cwd = env::current_dir().unwrap();
+ let mut cwd_clone = cwd.clone();
+ cwd_clone.push("target");
+ if cwd_clone.exists() {
+ // we are good
+ return Some(cwd);
+ }
+ cwd_clone.pop(); // target
+ cwd_clone.pop(); // ..
+ cwd_clone.push("target");
+ if cwd_clone.exists() {
+ // we are good
+ cwd_clone.pop();
+ return Some(cwd_clone);
+ }
+ None
+ }
+}
+
+mod xcode {
+ use std::fs;
+
+ use cargo_toml::{Manifest, Product};
+
+ use crate::macos::cargo;
+
+ pub(crate) fn build(project: &str, platform: &str, conf: &str, target: &str) {
+ std::process::Command::new("xcodebuild")
+ .args(["-project", project])
+ .args(["-destination", &format!("generic/platform={platform}")])
+ .args(["-configuration", conf])
+ .args(["-scheme", "box", "-quiet"])
+ .args(["-derivedDataPath", target])
+ // .args(env_args)
+ .status()
+ .unwrap();
+ }
+
+ #[derive(clap::Args, Debug)]
+ pub(crate) struct ProjArgs {
+ #[arg(long)]
+ pub(crate) bin: Option,
+ #[arg(long)]
+ pub(crate) example: Option,
+ #[arg(long)]
+ pub(crate) dep: Option,
+ }
+
+ fn find_product<'a>(
+ mans: &'a [Manifest],
+ args: &ProjArgs,
+ ) -> Option<(&'a Manifest, &'a Product)> {
+ let (is_bin, name, nothing, not_found, available) = if args.example.is_some() {
+ (
+ false,
+ args.example.as_ref(),
+ "No examples.",
+ "No examples found with name",
+ "Available examples:",
+ )
+ } else {
+ (
+ true,
+ args.bin.as_ref(),
+ "No binaries.",
+ "No binary found with name",
+ "Available binaries:",
+ )
+ };
+
+ let products = |man: &'a Manifest| -> &'a [Product] {
+ if is_bin {
+ &man.bin
+ } else {
+ &man.example
+ }
+ };
+
+ if let Some(product_name) = name.as_ref() {
+ let mut count = 0;
+ for man in mans {
+ for product in products(man) {
+ if product.name.as_ref() == name {
+ return Some((man, product));
+ }
+ count += 1;
+ }
+ }
+ if count == 0 {
+ println!("{nothing}");
+ } else {
+ println!("{not_found} `{product_name}`");
+ println!("{available}");
+ for man in mans {
+ for product in products(man) {
+ if let Some(name) = product.name.as_ref() {
+ println!("\t{name}");
+ }
+ }
+ }
+ }
+
+ return None;
+ }
+
+ for man in mans {
+ for product in man.bin.iter() {
+ if product.name.is_some() {
+ return Some((man, product));
+ }
+ }
+ }
+
+ println!("No binaries");
+
+ None
+ }
+
+ pub(crate) fn proj(args: ProjArgs) {
+ let (mut path, mans, _ws) = cargo::manifests().unwrap();
+ let product = if args.dep.is_some() {
+ &Product {
+ path: None,
+ name: args.dep.clone(),
+ test: false,
+ doctest: false,
+ bench: false,
+ doc: false,
+ plugin: false,
+ proc_macro: false,
+ harness: true,
+ edition: None,
+ crate_type: vec![],
+ required_features: vec![],
+ }
+ } else {
+ let Some((_man, product)) = find_product(&mans, &args) else {
+ return;
+ };
+ product
+ };
+
+ path.push(".box");
+ _ = dotenv::from_filename(".box");
+ path.pop();
+
+ let Ok(box_org_id) = std::env::var("BOX_ORG_ID") else {
+ println!("BOX_ORG_ID env is required. You can add it .box file");
+ return;
+ };
+ let Ok(dev_team_id) = std::env::var("DEVELOPMENT_TEAM") else {
+ println!("DEVELOPMENT_TEAM env is required. You can add it .box file");
+ println!("use cidre-box teams command to list available team ids");
+ return;
+ };
+
+ path.push("target/boxes");
+ if args.example.is_some() {
+ path.push("examples")
+ }
+ if args.dep.is_some() {
+ path.push("deps")
+ }
+ let product_name = product.name.as_ref().unwrap();
+ path.push(product_name);
+
+ fs::create_dir_all(&path).unwrap();
+ path.push("box.entitlements");
+ fs::write(&path, include_str!("../box/box.entitlements")).unwrap();
+ path.pop();
+ path.push("cfg.xcconfig");
+ fs::write(&path, include_str!("../box/cfg.xcconfig")).unwrap();
+ path.pop();
+ path.push("box.xcconfig");
+ fs::write(
+ &path,
+ format!(
+ r#"
+PRODUCT_NAME = {product_name}
+BOX_ID = {product_name}
+DEVELOPMENT_TEAM = {dev_team_id}
+BOX_ORG_ID = {box_org_id}
+"#
+ ),
+ )
+ .unwrap();
+
+ path.pop();
+
+ path.push("box.xcodeproj");
+ fs::create_dir_all(&path).unwrap();
+ path.push("project.pbxproj");
+ fs::write(&path, include_str!("../box/box.xcodeproj/project.pbxproj")).unwrap();
+ path.pop();
+ path.push("xcshareddata/xcschemes");
+ fs::create_dir_all(&path).unwrap();
+ path.push("box.xcscheme");
+ fs::write(
+ &path,
+ include_str!("../box/box.xcodeproj/xcshareddata/xcschemes/box.xcscheme"),
+ )
+ .unwrap();
+ }
+}
+
+mod device_ctl {
+ use std::{env, fs, process};
+
+ fn run_cmd(args: &[&str]) -> String {
+ let json_output_path = env::temp_dir().join(format!("devicectl-{}.json", process::id()));
+ let mut child = process::Command::new("xcrun")
+ .args(["devicectl", "-q", "--json-output"])
+ .arg(&json_output_path)
+ .args(args)
+ .spawn()
+ .unwrap();
+ child.wait().unwrap();
+ let buf = fs::read_to_string(&json_output_path).unwrap();
+ let _ = fs::remove_file(json_output_path);
+ buf
+ }
+
+ pub(crate) fn install_app(device_id: &str, bundle: &str) {
+ run_cmd(&["device", "install", "app", "-d", device_id, bundle]);
+ }
+
+ pub(crate) fn run_app(device_id: &str, id: &str, args: &[String]) {
+ let mut args_vec = vec![
+ "device",
+ "process",
+ "launch",
+ "--console",
+ "-d",
+ device_id,
+ "--terminate-existing",
+ id,
+ ];
+ for s in args {
+ args_vec.push(&s);
+ }
+ let buf = run_cmd(&args_vec);
+ let run = serde_json::from_str::(&buf).unwrap();
+ let code = match (run.result.termination.signal, run.result.termination.code) {
+ (Some(signal), None) => signal,
+ (None, Some(code)) => code,
+ _ => 0,
+ };
+ std::process::exit(code);
+ }
+
+ pub(crate) fn list_devices() {
+ let buf = run_cmd(&["list", "devices"]);
+ let list = serde_json::from_str::(&buf).unwrap();
+ println!("{:#?}", list.result.devices);
+ }
+
+ mod json {
+ use serde::Deserialize;
+ use std::borrow::Cow;
+
+ #[derive(Deserialize, Debug)]
+ pub(crate) struct AppRun {
+ pub(crate) result: AppRunResult,
+ }
+
+ #[derive(Deserialize, Debug)]
+ pub(crate) struct AppRunResult {
+ #[serde(rename = "terminationResult")]
+ pub(crate) termination: AppRunTermination,
+ }
+
+ #[derive(Deserialize, Debug)]
+ pub(crate) struct AppRunTermination {
+ #[serde(rename = "terminatingSignal")]
+ pub(crate) signal: Option,
+ #[serde(rename = "exitCode")]
+ pub(crate) code: Option,
+ }
+
+ #[derive(Deserialize, Debug)]
+ pub(crate) struct DeviceList<'a> {
+ #[serde(borrow)]
+ pub(crate) result: ListResult<'a>,
+ }
+
+ #[derive(Deserialize, Debug)]
+ pub(crate) struct ListResult<'a> {
+ #[serde(borrow)]
+ pub(crate) devices: Vec>,
+ }
+
+ // #[serde(rename_all = "camelCase")]
+ #[derive(Deserialize, Debug)]
+ pub(crate) struct Device<'a> {
+ #[serde(rename = "identifier")]
+ pub(crate) id: &'a str,
+
+ #[serde(borrow)]
+ #[serde(rename = "deviceProperties")]
+ pub(crate) props: DeviceProps<'a>,
+
+ // pub(crate) visibility_class: &'a str,
+ #[serde(borrow)]
+ #[serde(rename = "connectionProperties")]
+ pub(crate) connection: ConnectionProps<'a>,
+
+ #[serde(borrow)]
+ #[serde(rename = "hardwareProperties")]
+ pub(crate) hardware: HwProps<'a>,
+ }
+
+ #[derive(Deserialize, Debug)]
+ pub(crate) struct DeviceProps<'a> {
+ pub(crate) name: Cow<'a, str>,
+ #[serde(rename = "developerModeStatus")]
+ pub(crate) dev_mode_status: Option<&'a str>,
+ }
+
+ #[derive(Deserialize, Debug)]
+ #[serde(rename_all = "camelCase")]
+ pub(crate) struct HwProps<'a> {
+ pub(crate) device_type: &'a str,
+ pub(crate) platform: &'a str,
+ pub(crate) udid: &'a str,
+ }
+
+ #[derive(Deserialize, Debug)]
+ #[serde(rename_all = "camelCase")]
+ pub(crate) struct ConnectionProps<'a> {
+ pub(crate) pairing_state: &'a str,
+ }
+ }
+}
diff --git a/cidre/examples/nw-connection/main.rs b/cidre/examples/nw-connection/main.rs
index aa9f3014..aefd872a 100644
--- a/cidre/examples/nw-connection/main.rs
+++ b/cidre/examples/nw-connection/main.rs
@@ -75,5 +75,5 @@ pub use macos::main;
#[cfg(not(target_os = "macos"))]
fn main() {
- todo!()
+ println!("todo");
}
diff --git a/cidre/src/api.rs b/cidre/src/api.rs
index 3d5bb008..f7e3a371 100644
--- a/cidre/src/api.rs
+++ b/cidre/src/api.rs
@@ -245,6 +245,7 @@ mod tests {
assert!(SHOULD_BE_FOUND.get_var().unwrap().len() > 0);
}
+ #[cfg(target_os = "macos")]
#[test]
fn version() {
assert!(api::macos_available("13.0"));
@@ -253,4 +254,14 @@ mod tests {
assert!(!api::version!(visionos = 1.0));
assert!(!api::version!(macos = 20.0));
}
+
+ #[cfg(target_os = "ios")]
+ #[test]
+ fn version() {
+ assert!(api::ios_available("18.0"));
+ assert!(api::ios_available("18.2"));
+ assert!(api::version!(macos = 13.0, ios = 14.0));
+ assert!(!api::version!(visionos = 1.0));
+ assert!(!api::version!(macos = 20.0));
+ }
}
diff --git a/cidre/src/at/audio/unit/component.rs b/cidre/src/at/audio/unit/component.rs
index 92df699b..e46443ef 100644
--- a/cidre/src/at/audio/unit/component.rs
+++ b/cidre/src/at/audio/unit/component.rs
@@ -1450,6 +1450,7 @@ impl audio::Component {
#[cfg(test)]
mod tests {
+ #[cfg(target_os = "macos")]
use std::{f32::consts::PI, ffi::c_void};
use au::Element;
diff --git a/cidre/src/at/audio/unit/multi_channel_mixer.rs b/cidre/src/at/audio/unit/multi_channel_mixer.rs
index 7aa3d92c..3996083f 100644
--- a/cidre/src/at/audio/unit/multi_channel_mixer.rs
+++ b/cidre/src/at/audio/unit/multi_channel_mixer.rs
@@ -362,8 +362,14 @@ mod tests {
assert!(!mixer.is_input_enabled(0).unwrap());
// default volumes are zero
+ #[cfg(target_os = "macos")]
assert_eq!(0.0f32, mixer.input_volume(0).unwrap());
+ #[cfg(target_os = "ios")]
+ assert_eq!(1.0f32, mixer.input_volume(0).unwrap());
+ #[cfg(target_os = "macos")]
assert_eq!(0.0f32, mixer.output_volume().unwrap());
+ #[cfg(target_os = "ios")]
+ assert_eq!(1.0f32, mixer.output_volume().unwrap());
// metering is not enabled by default, so we can't access params
assert_eq!(false, mixer.is_input_metering_enabled(0).unwrap());
diff --git a/cidre/src/sec/item.rs b/cidre/src/sec/item.rs
index d7fb231b..802b1279 100644
--- a/cidre/src/sec/item.rs
+++ b/cidre/src/sec/item.rs
@@ -299,6 +299,7 @@ pub mod return_data {
}
}
+#[cfg(target_os = "macos")]
#[cfg(test)]
mod tests {
use crate::{arc, cf, sec};