Repository: time-rs/time (language: rust)

Issue:
Can't build with no_std because of cfg-if dependency
```
 └─  cargo c
    Updating crates.io index
       F       Fetch [================>                                      ]  31.39%                                                                                                        Downloaded time-macros v0.1.0
  Downloaded time-macros-impl v0.1.0
  Downloaded standback v0.2.2
  Downloaded proc-macro-hack v0.5.15
  Downloaded time v0.2.9
  Downloaded 5 crates (123.9 KB) in 0.91s
   Compiling syn v1.0.17
   Compiling rustversion v1.0.2
   Compiling standback v0.2.2
   Compiling proc-macro-hack v0.5.15
    Checking cfg-if v0.1.10
error[E0463]: can't find crate for `std`
  |
  = note: the `thumbv7em-none-eabihf` target may not be installed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0463`.
error: could not compile `standback`.

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed
```


Discussion / hints:
Using the new feature resolver doesn't work either, this time failing because of the macros crate:

```
 └─  cargo c -Zfeatures
    Checking standback v0.2.2
   Compiling time v0.2.9
    Checking time-macros v0.1.0
error[E0463]: can't find crate for `std`
  |
  = note: the `thumbv7em-none-eabihf` target may not be installed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0463`.
error: could not compile `standback`.

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error[E0463]: can't find crate for `std`
  |
  = note: the `thumbv7em-none-eabihf` target may not be installed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0463`.
error: could not compile `time-macros`.

To learn more, run the command again with --verbose.
warning: build failed, waiting for other jobs to finish...
error: build failed
```
What's in your `Cargo.toml`? And what are you trying to target?

I'll look into this once I get on my laptop.
```
time = { version = "0.2", default-features = false }
```

I'm trying to target a cortex-m so the toolchain is `thumbv7em-none-eabihf` where no `std` is available at all, including at build time apparently. So this won't work: https://github.com/time-rs/time/blob/02848913be1d836f72f270f070975397138a8d6e/time-macros-impl/src/ext.rs#L3

I'd be happy to have the macros behind a feature gate anyway, we won't be using them and the extra syn/proc_macro dependencies inflate compile time.
Looking at the error message again, it is failing on the `standback` crate, not `time-macros-impl`. This isn't surprising, since it would be compiled first. In the line you referenced, that is trivially replaceable with `core`.

Ultimately, the build scripts is where the issue lies. In `time`, it's not strictly necessary to use the build script — it's more for convenience on my end. In `standback`, using `std` in `build.rs` is necessary to be able to gate everything appropriately. This _may_ be possible to work around using a "force" feature, though this would lead to duplication in the binary (increasing size) unless LLVM is able to optimize it away.

Edit: Actually, using `std` in the time crate is necessary to allow for compilation with `cargo web`.
Could we feature gate standback somehow? I don't think it's unreasonable to disable polyfills if I decide to compile on nightly. 
I'd really prefer not to have to handroll a time library just so I can use it on embedded :/
Unfortunately disabling standback conditionally would require placing it behind a Cargo feature flag, which is a breaking change. Cargo doesn't support non-nightly dependencies or anything of that sort.

I looked into using the version_check crate, but that actually has the same problem as my current approach. I'll talk to Sergio to see if anything can be done here — I think aggressively gating tier 2/3 targets that don't have a build for std might work. Short of that, I've got nothing.

Sorry if this seems complicated; it's a side effect of the way build scripts are designed.
So I checked and [my target does seem to have a `rust-std` build](https://rust-lang.github.io/rustup-components-history/thumbv7em-none-eabihf.html), I could tell it was downloading it when I did `rustup update`.

A similar issue that links back to here https://github.com/deltachat/deltachat-core-rust/issues/1394 seems to indicate that I might have been running a nightly version where `rust-std` was unavailable. I'll double check in the morning.

That does make a bit more sense though, I'd expect the compiler to use std during the build step even if the actual library uses `no_std`.
I was able to get everything running after removing standback and putting macros under a feature flag: https://github.com/archseer/time/commit/7594c2315834209ccb62e7770c91b4818ea640f0
If that works for you, that's great. Unfortunately those changes are not feasible for the crate as a whole. Removing standback requires bumping the MSRV — that's why I created standback in the first place. Placing the macros behind a feature flag is a breaking change. Given that macros should be used in most situations (avoidance of unnecessary `unwrap`s), I'm not even sure doing that would be the correct decision. 

Don't get me wrong, I'd like to make this work on targets without a build for the standard library. I just don't think it's currently possible given the constraints across the ecosystem.
Just ran into this issue myself for an embedded thumbv6m target.  I had assumed that since the crate listed `#![no_std]` support quite prominently then it would work for my use.  If it is not feasible to use this crate on targets without stdlib can we at least get a big warning in the docs to this point?
> Don't get me wrong, I'd like to make this work on targets without a build for the standard library.

That's just the thing, I now confirmed that my target does have a standard library, we just build with `#![no_std]` which disables it. I think the build is likely broken for all `no_std` targets, but I can't explain why it passes on the CI.

> Unfortunately those changes are not feasible for the crate as a whole.

No worries, that's understandable! I provided the commit above as a reference so the changes could potentially be made on the next major version bump. I'll probably be maintaining my fork for the time being since the diff is so minimal.

> Given that macros should be used in most situations (avoidance of unnecessary unwraps), I'm not even sure doing that would be the correct decision.

I can see that being the case in some places but we're only needing the library to parse dates (from ASN.1) and then compare those. Disabling macros is a reasonable tradeoff for us especially since syn/proc_macro_hack inflate compile times quite significantly.
@berkowski I can definitely add a disclaimer about `#![no_std]` support if I can't get things to work in the long run.

@archseer That's certainly a valid situation that wouldn't need the macros in any fashion. I can weigh the pros and cons of more aggressive feature-gating for a 0.3 release. The one disadvantage that immediately comes to mind is that the most examples use macros.

The proc macro dependencies are one-off, given Rust's incremental build capabilities, and have no runtime overhead. At least on my machine, it doesn't take terribly long, but I suppose it's all relative to the project you're working on.

And FYI, the reason it passes on CI is because none of those targets are tested. The extent of what is tested is Windows, Linux, and MacOS on 1.34 (MSRV), 1.36 (MSRV with `#![no_std]` support), and the current stable.
@jhpratt Thanks, a big disclaimer would be useful.  Out of curiosity, what's the goal of supporting `#![no_std]` on non-embedded systems?
It's little extra effort and allows usage in other `#[no_std]` crates, which has more use cases than I would imagine.

Fix patch:
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index c8b6f20c9..5677112ad 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -53,6 +53,49 @@ jobs:
           command: check
           args: --features serde,deprecated,panicking-api,rand
 
+  check-embedded:
+    name: Type checking (embedded)
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        rust: [1.34.0, 1.36.0, stable]
+
+    steps:
+      - name: Checkout sources
+        uses: actions/checkout@v2
+
+      - name: Cache target
+        uses: actions/cache@v1
+        env:
+          cache-name: target
+        with:
+          path: ./target
+          key: ubuntu-latest-typecheck-target-${{ matrix.rust }}-thumbv7em-none-eabihf
+
+      - name: Install toolchain
+        uses: actions-rs/toolchain@v1
+        with:
+          profile: minimal
+          toolchain: ${{ matrix.rust }}
+          target: thumbv7em-none-eabihf
+          override: true
+
+      # ensure `#![no_std]` support
+      - name: Run `cargo check --no-default-features`
+        uses: actions-rs/cargo@v1
+        with:
+          command: check
+          args: --no-default-features --target thumbv7em-none-eabihf
+        if: matrix.rust != '1.34.0' # alloc is unstable in 1.34
+
+      # `#![no_std]` with serde, rand
+      - name: Run `cargo check --no-default-features --features serde,rand`
+        uses: actions-rs/cargo@v1
+        with:
+          command: check
+          args: --no-default-features --features serde,rand --target thumbv7em-none-eabihf
+        if: matrix.rust != '1.34.0' # alloc is unstable in 1.34
+
   test:
     name: Test suite
     runs-on: ${{ matrix.os }}
diff --git a/Cargo.toml b/Cargo.toml
index 503e8b461..dfee38728 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,7 +32,7 @@ cfg-if = "0.1.10"
 rand = { version = "0.7", optional = true, default-features = false }
 rustversion = "1"
 serde = { version = "1", optional = true, default-features = false, features = ["derive"] }
-standback = { version = "0.2", default-features = false }
+standback = { version = "0.2.3", default-features = false }
 time-macros = { version = "0.1", path = "time-macros" }
 
 [workspace]
diff --git a/build.rs b/build.rs
index 43a3451d1..3b5ee3983 100644
--- a/build.rs
+++ b/build.rs
@@ -14,12 +14,6 @@ fn features(features: &[(&str, &str)]) {
     }
 }
 
-fn no_std() {
-    if env::var("CARGO_FEATURE_STD").is_err() {
-        println!("cargo:rustc-cfg=no_std");
-    }
-}
-
 #[rustversion::since(1.40.0)]
 fn non_exhaustive() {
     println!("cargo:rustc-cfg=supports_non_exhaustive");
@@ -38,6 +32,5 @@ fn main() {
         ("SERDE", "serde"),
         ("__DOC", "docs"),
     ]);
-    no_std();
     non_exhaustive();
 }
diff --git a/src/format/format.rs b/src/format/format.rs
index 89e9d2313..b739703d6 100644
--- a/src/format/format.rs
+++ b/src/format/format.rs
@@ -1,6 +1,6 @@
 //! The `Format` struct and its implementations.
 
-#[cfg(no_std)]
+#[cfg(not(std))]
 use crate::internal_prelude::*;
 
 /// Various well-known formats, along with the possibility for a custom format
diff --git a/src/format/parse_items.rs b/src/format/parse_items.rs
index c7485caa0..07eb65426 100644
--- a/src/format/parse_items.rs
+++ b/src/format/parse_items.rs
@@ -1,7 +1,7 @@
 //! Parse formats used in the `format` and `parse` methods.
 
 use crate::format::{FormatItem, Padding, Specifier};
-#[cfg(no_std)]
+#[cfg(not(std))]
 use crate::internal_prelude::*;
 
 /// Parse the formatting string. Panics if not valid.
diff --git a/src/lib.rs b/src/lib.rs
index 9c8c3f8d9..a25eb0bb3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -141,7 +141,7 @@
 //! | `0`              | Pad with zeros  | `%0d` => `05` |
 
 #![cfg_attr(docs, feature(doc_cfg))]
-#![cfg_attr(no_std, no_std)]
+#![cfg_attr(not(std), no_std)]
 #![deny(
     unsafe_code, // Used when interacting with system APIs
     anonymous_parameters,
@@ -203,7 +203,7 @@ compile_error!("The `__doc` feature requires a nightly compiler, and is for inte
 #[rustversion::before(1.34.0)]
 compile_error!("The time crate has a minimum supported rust version of 1.34.0.");
 
-#[cfg(no_std)]
+#[cfg(not(std))]
 #[rustversion::before(1.36.0)]
 compile_error!(
     "Using the time crate without the standard library enabled requires a global allocator. This \
@@ -218,7 +218,7 @@ macro_rules! format_conditional {
     };
 
     ($first_conditional:ident, $($conditional:ident),*) => {{
-        #[cfg(no_std)]
+        #[cfg(not(std))]
         let mut s = alloc::string::String::new();
         #[cfg(std)]
         let mut s = String::new();
@@ -457,7 +457,7 @@ pub mod prelude {
 mod internal_prelude {
     #![allow(unused_imports)]
 
-    #[cfg(no_std)]
+    #[cfg(not(std))]
     extern crate alloc;
 
     #[cfg(std)]
@@ -469,7 +469,7 @@ mod internal_prelude {
         PrimitiveDateTime, Time, UtcOffset,
         Weekday::{self, Friday, Monday, Saturday, Sunday, Thursday, Tuesday, Wednesday},
     };
-    #[cfg(no_std)]
+    #[cfg(not(std))]
     pub(crate) use alloc::{
         borrow::ToOwned,
         boxed::Box,
diff --git a/time-macros/src/lib.rs b/time-macros/src/lib.rs
index 861ef1e13..0eae89ff6 100644
--- a/time-macros/src/lib.rs
+++ b/time-macros/src/lib.rs
@@ -1,3 +1,5 @@
+#![no_std]
+
 use proc_macro_hack::proc_macro_hack;
 
 #[proc_macro_hack]


Test patch: