robocop

Checks KYC attributes against sanction lists
Log | Files | Refs | Submodules | README | LICENSE

build.rs (4070B)


      1 // This file is part of Robocop
      2 //
      3 // Robocop is free software: you can redistribute it and/or modify
      4 // it under the terms of the GNU General Public License as published by
      5 // the Free Software Foundation, either version 3 of the License, or
      6 // (at your option) any later version.
      7 //
      8 // Robocop is distributed in the hope that it will be useful,
      9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11 // GNU General Public License for more details.
     12 //
     13 // You should have received a copy of the GNU General Public License
     14 // along with this program. If not, see <https://www.gnu.org/licenses/>.
     15 //
     16 // Copyright (C) 2026 Taler Systems SA
     17 
     18 use std::env;
     19 use std::fs;
     20 use std::path::{Path, PathBuf};
     21 use std::process::Command;
     22 
     23 fn git(root: &Path, args: &[&str]) -> Result<String, String> {
     24     let output = Command::new("git")
     25         .args(args)
     26         .current_dir(root)
     27         .env_remove("GIT_DIR")
     28         .env_remove("GIT_WORK_TREE")
     29         .env_remove("GIT_COMMON_DIR")
     30         .output()
     31         .map_err(|e| format!("could not run git: {e}"))?;
     32     if !output.status.success() {
     33         return Err(format!(
     34             "git {}: {}",
     35             args.join(" "),
     36             String::from_utf8_lossy(&output.stderr).trim()
     37         ));
     38     }
     39     String::from_utf8(output.stdout)
     40         .map(|value| value.trim().to_owned())
     41         .map_err(|e| format!("invalid Git output: {e}"))
     42 }
     43 
     44 fn watch(path: &Path) {
     45     println!("cargo:rerun-if-changed={}", path.display());
     46 }
     47 
     48 fn git_version(root: &Path) -> Result<String, String> {
     49     // An archive can be unpacked inside another checkout. Only use our own Git
     50     // metadata, including the .git file used by linked worktrees/submodules.
     51     let marker = root.join(".git");
     52     if !marker.exists() {
     53         return Err("source tree has no Git metadata".into());
     54     }
     55     if marker.is_file() {
     56         watch(&marker);
     57     }
     58     let toplevel = git(root, &["rev-parse", "--show-toplevel"])?;
     59     if Path::new(&toplevel)
     60         .canonicalize()
     61         .map_err(|e| e.to_string())?
     62         != root
     63     {
     64         return Err("Git metadata belongs to another source tree".into());
     65     }
     66 
     67     // Resolve paths through Git: worktree HEAD is private, while refs and
     68     // packed-refs live in the common Git directory. Watching all loose refs
     69     // also catches new/deleted tags, even when HEAD itself does not move.
     70     for name in ["HEAD", "refs", "packed-refs", "shallow"] {
     71         let path = PathBuf::from(git(
     72             root,
     73             &["rev-parse", "--path-format=absolute", "--git-path", name],
     74         )?);
     75         // refs exists even when empty. Packing refs removes loose files there,
     76         // which reruns this script and adds the newly created packed-refs file.
     77         if path.exists() || name == "HEAD" {
     78             watch(&path);
     79         }
     80     }
     81     git(root, &["describe", "--tags", "--always", "--abbrev=8"])
     82 }
     83 
     84 fn main() -> Result<(), String> {
     85     let manifest =
     86         PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").ok_or("CARGO_MANIFEST_DIR is missing")?);
     87     let root = manifest
     88         .canonicalize()
     89         .map_err(|e| format!("could not locate source root: {e}"))?;
     90     watch(&manifest.join("build.rs"));
     91     let version = match git_version(&root) {
     92         Ok(version) => version,
     93         Err(git_error) => {
     94             let path = root.join(".version");
     95             watch(&path);
     96             // Recheck Git availability on later builds of an extracted tree.
     97             watch(&root.join(".git"));
     98             fs::read_to_string(&path).map_err(|e| format!(
     99                 "cannot determine program version: {git_error}; could not read {}: {e}. Source archives must contain a .version file",
    100                 path.display()
    101             ))?
    102         }
    103     };
    104     let version = version.trim();
    105     if version.is_empty() || version.contains(['\n', '\r', '\0']) {
    106         return Err("program version must be a nonempty single line".into());
    107     }
    108     println!("cargo:rustc-env=BUILD_VERSION={version}");
    109     Ok(())
    110 }