no-std-compat-0.4.1/.cargo_vcs_info.json0000644000000001121371373311500136170ustar00{ "git": { "sha1": "23b989b17963096cc782e6f724d4e91a45132477" } } no-std-compat-0.4.1/.gitignore010064400017500000144000000000341364202234000143770ustar0000000000000000target **/*.rs.bk Cargo.lockno-std-compat-0.4.1/Cargo.toml0000644000000022451371373311500116260ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "no-std-compat" version = "0.4.1" authors = ["jD91mZM2 "] exclude = ["example-crates/**/*"] description = "A `#![no_std]` compatibility layer that will make porting your crate to no_std *easy*." readme = "README.md" categories = ["no-std"] license = "MIT" repository = "https://gitlab.com/jD91mZM2/no-std-compat" [profile.dev] panic = "abort" [dependencies.hashbrown] version = "0.8.1" optional = true [dependencies.spin] version = "0.5.2" optional = true [dev-dependencies.libc] version = "0.2.74" [features] alloc = [] compat_hash = ["hashbrown"] compat_macros = [] compat_sync = ["spin"] std = [] unstable = [] no-std-compat-0.4.1/Cargo.toml.orig010064400017500000144000000015071371373274100153200ustar0000000000000000[package] name = "no-std-compat" version = "0.4.1" authors = ["jD91mZM2 "] exclude = [ "example-crates/**/*" ] description = "A `#![no_std]` compatibility layer that will make porting your crate to no_std *easy*." repository = "https://gitlab.com/jD91mZM2/no-std-compat" categories = [ "no-std" ] license = "MIT" readme = "README.md" edition = "2018" [dependencies] hashbrown = { version = "0.8.1", optional = true } spin = { version = "0.5.2", optional = true } [dev-dependencies] libc = "0.2.74" [profile.dev] panic = "abort" [features] alloc = [] std = [] unstable = [] # This will sadly also add hashbrown even if overriden by std. Cargo # does not seem to provide any cfg(compat_hash && !std) functionality. # Luckily, hashbrown is really small. compat_hash = ["hashbrown"] compat_sync = ["spin"] compat_macros = [] no-std-compat-0.4.1/LICENSE010064400017500000144000000020511364202234000134150ustar0000000000000000MIT License Copyright (c) 2019 jD91mZM2 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. no-std-compat-0.4.1/README.md010064400017500000144000000132161364202234000136740ustar0000000000000000# `no-std-compat` A `#![no_std]` compatibility layer that will make porting your crate to no_std *easy*. It supports stable rust by default since no-std-compat version 0.2.0 ([See issue #2](https://gitlab.com/jD91mZM2/no-std-compat/issues/2)). ## Why this exists In Rust, you can disable the standard library (see [here](https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html)). Doing this gets rid of the normal `std` standard library and instead adds `core`, with an option to also add `alloc` for things requiring memory allocation. Using `core` + `alloc` results in something similar to the `std`, and many things using `std` can already be "ported" to use `core` + `alloc`. But *every single library* written in rust needs to be updated. This is because the norm is to use `std`. Using core needs someone to break the norm, often only behind a feature flag. Compare this to Web Assembly, where almost only a few low-level crates like `rand` needs to care, because everything is still under `std` even though some features don't work there. Many crates migrating to `#![no_std]` today write a small module called `std` that forwards imports libcore and liballoc together. These efforts should be **unified**. We're stronger if not every single one of us needs to hit and figure out how to fix the same errors. ## Usage This library is designed to require as few lines of code as possible, so that these can be copy-pasted to a bunch of different libraries. My goal is to turn more crates into `#![no_std]` compatible. It also has in mind to support the std, as well as supporting no std, meaning you should only need few conditional compilation attributes. *Examples can be found in the `example-crates/` folder.* 1​. Add this crate to Cargo.toml, and enable any features you want to require (see next section). `Cargo.toml`: ```toml [dependencies] no-std-compat = { version = "...", features = [ "alloc" ] } ``` 2​. Optionally, add a `std` flag that pulls in the entire standard library and bypasses this compatibility crate. This is useful so you can use the standard library for debugging and for extra functionality for those who support it. The below code *optionally* adds the `std` feature as well to `no-std-compat`, which makes it just link to the standard library. `Cargo.toml`: ```toml [features] default = [ "std" ] # Default to using the std std = [ "no-std-compat/std" ] ``` 3​. Enable `no_std`, and import this crate renamed to `std`. This ensures all old imports still work on `no_std`. Even if you do want to use the std, enabling `no_std` is okay - `no-std-compat` will pull in std if you send the right feature flags anyway. You could, of course, use any other name than "std" here too. But this is what I would recommend. `src/lib.rs`: ```rust #![no_std] extern crate no_std_compat as std; ``` 4​. Import the prelude *in all files*. This is because in `no_std`, rust removes the `std` import and instead only imports the `core` prelude. That is: Currently, it doesn't import the `alloc` prelude on its own. This also imports macros and other needed stuff. `src/**.rs`: ```rust use std::prelude::v1::*; ``` ## Optional features - `alloc`: This feature pulls in `alloc` and exposes it in all the usual locations. I.e `std::collection` gets mapped to `alloc::collections` and all the allocation stuff is added to the prelude. - `std`: This feature pulls in the entire standard library and overrides all other features. This effectively bypasses this crate completely. This is here to avoid needing feature gates: Just forward your optional `std` feature to here, we handle the rest. - `unstable`: This feature also re-exports all unstable modules, which isn't possible to do unless you compile with nightly. Unless you need an unstable module, this crate supports stable rust. - `compat_hash`: This pulls in [hashbrown](https://github.com/rust-lang/hashbrown) (which is not HashDoS-resistant!! but #![no_std]). The point is so you can keep using the standard, safe, HashMap for those who have the standard library, and fall back to a less ideal alternative for those who do not. Be advised, however, that this used in a public function signature could be confusing and should perhaps be avoided. But that is up to you! - `compat_sync`: This pulls in [spin](https://github.com/mvdnes/spin-rs) and provides replacements for several things used in `std::sync`. - `compat_macros`: This feature adds dummy `println`, `eprintln`, `dbg`, etc. implementations that do absolutely nothing. The point is that any debug functions or other loggings that are not required for the library to function, just stay silent in `no_std`. ## Contributing ### Updating the glue Did you pull this crate and realize that it's outdated? Lucky for you, this crate came prepared. The glue can simply be regenerated with a python script. Make sure you have the rust source downloaded somewhere. With rustup, it's a non-issue: ```rust rustup component add rust-src ``` Now you can run `./generate.py > src/generated.rs`. If it chooses the wrong rust version or maybe crashes all together, you can manually specify the source directory with `--src`. It's that easy. You can also, of course, run `./generate.py --help` if you forgot the argument name. ### Updating the feature list If rust complains about a feature being required but not specified, or maybe about a feature being unused, this is because some imports are behind feature gates, and feature gates change. More often than not it is as trivial as adding or removing stuff from the long, long line in `src/lib.rs` that specifies features. Should only be a problem when using the `unstable` feature. no-std-compat-0.4.1/check.py010075500017500000144000000023061364202234000140450ustar0000000000000000#!/usr/bin/env python3 import subprocess import sys from pathlib import Path def powerset(input): if len(input) == 0: return [[]] pivot = input[0] subset = powerset(input[1:]) with_pivot = subset.copy() for i, set in enumerate(with_pivot): with_pivot[i] = [pivot] + set return subset + with_pivot def execute(args, **kwargs): cwd = "" if "cwd" in kwargs: cwd += str(kwargs["cwd"]) + "/ " print(cwd + "$ " + " ".join(args)) status = subprocess.run(args, **kwargs) if status.returncode != 0: sys.exit(1) def check(toolchain, features, **kwargs): for subset in powerset(features): feature_str = ",".join(subset) execute( ["cargo", "+" + toolchain, "check", "--features", feature_str], **kwargs ) features = [ "alloc", "std", # "unstable", "compat_hash", "compat_macros", ] check("stable", features) check("nightly", features + ["unstable"]) for dir in Path("example-crates").iterdir(): if not dir.joinpath("Cargo.toml").exists(): continue execute(["cargo", "test", "--features", "std"], cwd=dir) execute(["cargo", "test"], cwd="example-crates") no-std-compat-0.4.1/generate.py010075500017500000144000000106461371250412500145740ustar0000000000000000#!/usr/bin/env python3 import argparse import os import re import subprocess import sys # Parse arguments parser = argparse.ArgumentParser( description="Generate a std compatibility module" ) parser.add_argument("--src", help=( "Specify the location of the rust source code. The default is " "`$(rustc --print sysroot)/lib/rustlib/src/rust/src`" )) args = parser.parse_args() if args.src is None: output = subprocess.run(["rustc", "--print", "sysroot"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) args.src = os.path.join(output.stdout.decode("utf-8").strip(), "lib", "rustlib", "src", "rust", "src") # Read files modules_regex = re.compile( r"^(?:\S.*)?pub\s+(?:mod\s+|use\s+(?:[a-zA-Z_][a-zA-Z0-9_]*::)*)" r"([a-zA-Z_][a-zA-Z0-9_]*);", re.MULTILINE ) def modules(crate): root = os.path.join(args.src, crate) lib = os.path.join(root, "lib.rs") with open(lib) as f: contents = f.read() modules = dict() for match in modules_regex.finditer(contents): module = match.group(1) unstable = False path = os.path.join(root, module + ".rs") if not os.path.isfile(path): path = os.path.join(root, module, "mod.rs") try: with open(path, "r") as f: unstable = "#![unstable" in f.read() if unstable: print( f"Module '{module}' from '{crate}' appears unstable", file=sys.stderr ) except OSError: pass modules[module] = unstable return modules def generate(module, unstable, *namespaces): out = f"pub mod {module} {{\n" if module == "prelude": return None for namespace in namespaces: out += " " cfgs = [] if namespace != "core": cfgs.append(f"feature = \"{namespace}\"") if unstable: cfgs.append("feature = \"unstable\"") if len(cfgs) == 1: out += f"#[cfg({cfgs[0]})] " elif len(cfgs) > 1: out += "#[cfg(all(" + ", ".join(cfgs) + "))] " out += f"pub use __{namespace}::{module}::*;\n" if module == "collections": prefix = ( " #[cfg(all(" "feature = \"alloc\", " "feature = \"compat_hash\"" "))] pub use hashbrown::" ) out += ( prefix + "HashMap;\n" + prefix + "HashSet;\n" ) elif module == "sync": prefix = ( " #[cfg(all(" "feature = \"alloc\", " "feature = \"compat_sync\"" "))] pub use spin::" ) out += ( prefix + "Mutex;\n" + prefix + "MutexGuard;\n" + prefix + "Once;\n" + prefix + "RwLock;\n" + prefix + "RwLockReadGuard;\n" + prefix + "RwLockWriteGuard;\n" ) out += "}" return out core = modules("libcore") alloc = modules("liballoc") generated = {} core_keys = set(core.keys()) alloc_keys = set(alloc.keys()) for module in core_keys & alloc_keys: # TODO: separate these unstable = core[module] or alloc[module] generated[module] = generate(module, unstable, "core", "alloc") for module in core_keys - alloc_keys: unstable = core[module] generated[module] = generate(module, unstable, "core") for module in alloc_keys - core_keys: unstable = alloc[module] generated[module] = generate(module, unstable, "alloc") generated["prelude"] = """pub mod prelude { pub mod v1 { // Prelude pub use __core::prelude::v1::*; #[cfg(all(feature = "alloc", feature = "unstable"))] pub use __alloc::prelude::v1::*; #[cfg(all(feature = "alloc", not(feature = "unstable")))] pub use __alloc::{ borrow::ToOwned, boxed::Box, // UNSTABLE: slice::SliceConcatExt, string::String, string::ToString, vec::Vec, }; // Other imports #[cfg(feature = "alloc")] pub use __alloc::{format, vec}; #[cfg(feature = "compat_macros")] pub use crate::{print, println, eprint, eprintln, dbg}; } }""" print("""//! Generated by generate.py located at the repository root //! ./generate.py > src/generated.rs""") for module in sorted(generated.items(), key=lambda i: i[0]): print(module[1]) no-std-compat-0.4.1/src/generated.rs010064400017500000144000000114651371250417000155200ustar0000000000000000//! Generated by generate.py located at the repository root //! ./generate.py > src/generated.rs pub mod alloc { pub use __core::alloc::*; #[cfg(feature = "alloc")] pub use __alloc::alloc::*; } pub mod any { pub use __core::any::*; } pub mod arch { pub use __core::arch::*; } pub mod array { pub use __core::array::*; } pub mod ascii { pub use __core::ascii::*; } pub mod borrow { pub use __core::borrow::*; #[cfg(feature = "alloc")] pub use __alloc::borrow::*; } pub mod boxed { #[cfg(feature = "alloc")] pub use __alloc::boxed::*; } pub mod cell { pub use __core::cell::*; } pub mod char { pub use __core::char::*; } pub mod clone { pub use __core::clone::*; } pub mod cmp { pub use __core::cmp::*; } pub mod collections { #[cfg(feature = "alloc")] pub use __alloc::collections::*; #[cfg(all(feature = "alloc", feature = "compat_hash"))] pub use hashbrown::HashMap; #[cfg(all(feature = "alloc", feature = "compat_hash"))] pub use hashbrown::HashSet; } pub mod convert { pub use __core::convert::*; } pub mod default { pub use __core::default::*; } pub mod f32 { pub use __core::f32::*; } pub mod f64 { pub use __core::f64::*; } pub mod ffi { pub use __core::ffi::*; } pub mod fmt { pub use __core::fmt::*; #[cfg(feature = "alloc")] pub use __alloc::fmt::*; } pub mod future { pub use __core::future::*; } pub mod hash { pub use __core::hash::*; } pub mod hint { pub use __core::hint::*; } pub mod i128 { pub use __core::i128::*; } pub mod i16 { pub use __core::i16::*; } pub mod i32 { pub use __core::i32::*; } pub mod i64 { pub use __core::i64::*; } pub mod i8 { pub use __core::i8::*; } pub mod intrinsics { #[cfg(feature = "unstable")] pub use __core::intrinsics::*; } pub mod isize { pub use __core::isize::*; } pub mod iter { pub use __core::iter::*; } pub mod marker { pub use __core::marker::*; } pub mod mem { pub use __core::mem::*; } pub mod num { pub use __core::num::*; } pub mod ops { pub use __core::ops::*; } pub mod option { pub use __core::option::*; } pub mod panic { #[cfg(feature = "unstable")] pub use __core::panic::*; } pub mod panicking { #[cfg(feature = "unstable")] pub use __core::panicking::*; } pub mod pin { pub use __core::pin::*; } pub mod prelude { pub mod v1 { // Prelude pub use __core::prelude::v1::*; #[cfg(all(feature = "alloc", feature = "unstable"))] pub use __alloc::prelude::v1::*; #[cfg(all(feature = "alloc", not(feature = "unstable")))] pub use __alloc::{ borrow::ToOwned, boxed::Box, // UNSTABLE: slice::SliceConcatExt, string::String, string::ToString, vec::Vec, }; // Other imports #[cfg(feature = "alloc")] pub use __alloc::{format, vec}; #[cfg(feature = "compat_macros")] pub use crate::{print, println, eprint, eprintln, dbg}; } } pub mod primitive { pub use __core::primitive::*; } pub mod ptr { pub use __core::ptr::*; } pub mod raw { #[cfg(feature = "unstable")] pub use __core::raw::*; } pub mod raw_vec { #[cfg(all(feature = "alloc", feature = "unstable"))] pub use __alloc::raw_vec::*; } pub mod rc { #[cfg(feature = "alloc")] pub use __alloc::rc::*; } pub mod result { pub use __core::result::*; } pub mod slice { pub use __core::slice::*; #[cfg(feature = "alloc")] pub use __alloc::slice::*; } pub mod str { pub use __core::str::*; #[cfg(feature = "alloc")] pub use __alloc::str::*; } pub mod string { #[cfg(feature = "alloc")] pub use __alloc::string::*; } pub mod sync { pub use __core::sync::*; #[cfg(feature = "alloc")] pub use __alloc::sync::*; #[cfg(all(feature = "alloc", feature = "compat_sync"))] pub use spin::Mutex; #[cfg(all(feature = "alloc", feature = "compat_sync"))] pub use spin::MutexGuard; #[cfg(all(feature = "alloc", feature = "compat_sync"))] pub use spin::Once; #[cfg(all(feature = "alloc", feature = "compat_sync"))] pub use spin::RwLock; #[cfg(all(feature = "alloc", feature = "compat_sync"))] pub use spin::RwLockReadGuard; #[cfg(all(feature = "alloc", feature = "compat_sync"))] pub use spin::RwLockWriteGuard; } pub mod task { pub use __core::task::*; #[cfg(all(feature = "alloc", feature = "unstable"))] pub use __alloc::task::*; } pub mod time { pub use __core::time::*; } pub mod u128 { pub use __core::u128::*; } pub mod u16 { pub use __core::u16::*; } pub mod u32 { pub use __core::u32::*; } pub mod u64 { pub use __core::u64::*; } pub mod u8 { pub use __core::u8::*; } pub mod unicode { #[cfg(feature = "unstable")] pub use __core::unicode::*; } pub mod usize { pub use __core::usize::*; } pub mod vec { #[cfg(feature = "alloc")] pub use __alloc::vec::*; } no-std-compat-0.4.1/src/lib.rs010064400017500000144000000052671371250425300143350ustar0000000000000000#![no_std] #![cfg_attr(all(not(feature = "std"), feature = "unstable"), feature(core_intrinsics, core_panic, raw, unicode_internals))] #![cfg_attr(all(not(feature = "std"), feature = "alloc", feature = "unstable"), feature(alloc_prelude, raw_vec_internals, wake_trait))] // Can't use cfg_if! because it does not allow nesting :( // Actually, can't even generate #[cfg]s any other way because of // https://github.com/rust-lang/rust/pull/52234#issuecomment-486810130 // if #[cfg(feature = "std")] { #[cfg(feature = "std")] extern crate std; #[cfg(feature = "std")] pub mod prelude { pub mod v1 { pub use std::prelude::v1::*; // Macros aren't included in the prelude for some reason pub use std::{ format, vec, print, println, eprint, eprintln, dbg }; } } #[cfg(feature = "std")] pub use std::*; // } else { // The 2 underscores in the crate names are used to avoid // ambiguity between whether the user wants to use the public // module std::alloc or the private crate no_std_compat::alloc // (see https://gitlab.com/jD91mZM2/no-std-compat/issues/1) // if #[cfg(feature = "alloc")] { #[cfg(all(not(feature = "std"), feature = "alloc"))] extern crate alloc as __alloc; // } #[cfg(not(feature = "std"))] extern crate core as __core; #[cfg(not(feature = "std"))] mod generated; #[cfg(not(feature = "std"))] pub use self::generated::*; // if #[cfg(feature = "compat_macros")] { #[cfg(all(not(feature = "std"), feature = "compat_macros"))] #[macro_export] macro_rules! print { () => {{}}; ($($arg:tt)+) => {{ // Avoid unused arguments complaint. This surely must get // optimized away? TODO: Verify that let _ = format_args!($($arg)+); }}; } #[cfg(all(not(feature = "std"), feature = "compat_macros"))] #[macro_export] macro_rules! println { ($($arg:tt)*) => { print!($($arg)*) } } #[cfg(all(not(feature = "std"), feature = "compat_macros"))] #[macro_export] macro_rules! eprint { ($($arg:tt)*) => { print!($($arg)*) } } #[cfg(all(not(feature = "std"), feature = "compat_macros"))] #[macro_export] macro_rules! eprintln { ($($arg:tt)*) => { print!($($arg)*) } } #[cfg(all(not(feature = "std"), feature = "compat_macros"))] #[macro_export] macro_rules! dbg { () => {}; ($($val:expr),+) => { ($($val),+) } } // } // }