gst_plugin_version_helper/
lib.rs

1// Copyright (C) 2019 Sajeer Ahamed <ahamedsajeer.15.15@cse.mrt.ac.lk>
2// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
3//
4// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
5//
6// SPDX-License-Identifier: MIT
7
8//! Extracts release for [GStreamer](https://gstreamer.freedesktop.org) plugin metadata
9//!
10//! See [`info`](fn.info.html) for details.
11//!
12//! This function is supposed to be used as follows in the `build.rs` of a crate that implements a
13//! plugin:
14//!
15//! ```rust,ignore
16//! gst_plugin_version_helper::info();
17//! ```
18//!
19//! Inside `lib.rs` of the plugin, the information provided by `info` are usable as follows:
20//!
21//! ```rust,ignore
22//! gst::plugin_define!(
23//!     the_plugin_name,
24//!     env!("CARGO_PKG_DESCRIPTION"),
25//!     plugin_init,
26//!     concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
27//!     "The Plugin's License",
28//!     env!("CARGO_PKG_NAME"),
29//!     env!("CARGO_PKG_NAME"),
30//!     env!("CARGO_PKG_REPOSITORY"),
31//!     env!("BUILD_REL_DATE")
32//! );
33//! ```
34
35mod git;
36
37use chrono::{Datelike, TimeZone};
38use std::convert::TryInto;
39use std::time::SystemTime;
40use std::{env, fs, path};
41
42/// Extracts release for GStreamer plugin metadata
43///
44/// Release information is first tried to be extracted from a git repository at the same
45/// place as the `Cargo.toml`, or one directory up to allow for Cargo workspaces. If no
46/// git repository is found, we assume this is a release.
47///
48/// - If extracted from a git repository, sets the `COMMIT_ID` environment variable to the short
49///   commit id of the latest commit and the `BUILD_REL_DATE` environment variable to the date of the
50///   commit.
51///
52/// - If not, `COMMIT_ID` will be set to the string `RELEASE` and the
53///   `BUILD_REL_DATE` variable will be set to the `package.metadata.gstreamer.release_date` key of
54///   `Cargo.toml`, if it exists.
55///
56/// - If not, `COMMIT_ID` will be set to the string `RELEASE` and the `BUILD_REL_DATE` variable
57///   will be set to the mtime of `Cargo.toml`. Note that the crates created by `cargo package` and
58///   `cargo publish` have bogus mtimes for all files and won't be used.
59///
60/// - If neither is possible, `COMMIT_ID` is set to the string `UNKNOWN` and `BUILD_REL_DATE` to the
61///   current date.
62///
63pub fn info() {
64    let crate_dir =
65        path::PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
66    let mut repo_dir = crate_dir.clone();
67
68    // First check for a git repository in the manifest directory and if there
69    // is none try one directory up in case we're in a Cargo workspace
70    let git_info = git::repo_hash(&repo_dir).or_else(move || {
71        repo_dir.pop();
72        git::repo_hash(&repo_dir)
73    });
74
75    // If there is a git repository, extract the version information from there.
76    // Otherwise assume this is a release and use Cargo.toml mtime as date.
77    let (commit_id, commit_date) = git_info.unwrap_or_else(|| {
78        let date = cargo_metadata_release_date(&crate_dir)
79            .or_else(|| cargo_mtime_date(&crate_dir))
80            .unwrap_or_else(chrono::Utc::now);
81        ("RELEASE".into(), date.format("%Y-%m-%d").to_string())
82    });
83
84    println!("cargo:rustc-env=COMMIT_ID={commit_id}");
85    println!("cargo:rustc-env=BUILD_REL_DATE={commit_date}");
86}
87
88fn cargo_metadata_release_date(crate_dir: &path::Path) -> Option<chrono::DateTime<chrono::Utc>> {
89    use std::io::prelude::*;
90
91    let mut cargo_toml = path::PathBuf::from(crate_dir);
92    cargo_toml.push("Cargo.toml");
93
94    let mut file = fs::File::open(&cargo_toml).ok()?;
95    let mut contents = String::new();
96    file.read_to_string(&mut contents).ok()?;
97
98    let doc = contents.parse::<toml_edit::DocumentMut>().ok()?;
99    let release_date = doc
100        .get("package")
101        .and_then(|package| package.as_table_like())
102        .and_then(|package| package.get("metadata"))
103        .and_then(|metadata| metadata.as_table_like())
104        .and_then(|metadata| metadata.get("gstreamer"))
105        .and_then(|gstreamer| gstreamer.as_table_like())
106        .and_then(|gstreamer| gstreamer.get("release_date"))
107        .and_then(|release_date| release_date.as_str())?;
108
109    let release_date = release_date.parse::<chrono::NaiveDate>().ok()?;
110    Some(chrono::DateTime::from_naive_utc_and_offset(
111        release_date.and_hms_opt(0, 0, 0)?,
112        chrono::Utc,
113    ))
114}
115
116fn cargo_mtime_date(crate_dir: &path::Path) -> Option<chrono::DateTime<chrono::Utc>> {
117    let mut cargo_toml = path::PathBuf::from(crate_dir);
118    cargo_toml.push("Cargo.toml");
119
120    let metadata = fs::metadata(&cargo_toml).ok()?;
121    let mtime = metadata.modified().ok()?;
122    let unix_time = mtime.duration_since(SystemTime::UNIX_EPOCH).ok()?;
123    let dt = chrono::Utc
124        .timestamp_opt(unix_time.as_secs().try_into().ok()?, 0)
125        .latest()?;
126
127    // FIXME: Work around https://github.com/rust-lang/cargo/issues/10285
128    if dt.date_naive().year() < 2015 {
129        return None;
130    }
131
132    Some(dt)
133}